1 /* this is for the DANO hack (new, simpler version than the BStringIO hack) */ 2 #include <BeBuild.h> 3 #ifdef B_BEOS_VERSION_DANO 4 #define private public 5 #include <Messenger.h> 6 #undef private 7 #endif 8 9 #include "AutoRaiseIcon.h" 10 #include <stdlib.h> 11 #include <DataIO.h> 12 #include <Screen.h> 13 #include <View.h> 14 #include <Debug.h> 15 16 17 extern "C" _EXPORT BView *instantiate_deskbar_item(void) 18 { 19 puts("Instanciating AutoRaise TrayView..."); 20 return (new TrayView); 21 } 22 23 24 long removeFromDeskbar(void *) 25 { 26 BDeskbar db; 27 if (db.RemoveItem(APP_NAME) != B_OK) 28 printf("Unable to remove AutoRaise from BDeskbar\n"); 29 30 return 0; 31 } 32 33 //************************************************** 34 35 ConfigMenu::ConfigMenu(TrayView *tv, bool useMag) 36 :BPopUpMenu("config_popup", false, false){ 37 38 BMenu *tmpm; 39 BMenuItem *tmpi; 40 BMessage *msg; 41 42 AutoRaiseSettings *s = tv->Settings(); 43 44 SetFont(be_plain_font); 45 46 47 BMenuItem *active = new BMenuItem("Active", new BMessage(MSG_TOOGLE_ACTIVE)); 48 active->SetMarked(s->Active()); 49 AddItem(active); 50 51 tmpm = new BMenu("Mode"); 52 tmpm->SetFont(be_plain_font); 53 54 msg = new BMessage(MSG_SET_MODE); 55 msg->AddInt32(AR_MODE, Mode_All); 56 tmpi = new BMenuItem("Default (all windows)", msg); 57 tmpi->SetMarked(s->Mode() == Mode_All); 58 tmpm->AddItem(tmpi); 59 60 msg = new BMessage(MSG_SET_MODE); 61 msg->AddInt32(AR_MODE, Mode_DeskbarOver); 62 tmpi = new BMenuItem("Deskbar only (over its area)", msg); 63 tmpi->SetMarked(s->Mode() == Mode_DeskbarOver); 64 #ifdef USE_DANO_HACK 65 tmpi->SetEnabled(false); 66 #endif 67 tmpm->AddItem(tmpi); 68 69 msg = new BMessage(MSG_SET_MODE); 70 msg->AddInt32(AR_MODE, Mode_DeskbarTouch); 71 tmpi = new BMenuItem("Deskbar only (touch)", msg); 72 tmpi->SetMarked(s->Mode() == Mode_DeskbarTouch); 73 tmpm->AddItem(tmpi); 74 75 76 tmpm->SetTargetForItems(tv); 77 BMenuItem *modem = new BMenuItem(tmpm); 78 modem->SetEnabled(s->Active()); 79 AddItem(modem); 80 81 tmpm = new BMenu("Inactive behaviour"); 82 tmpm->SetFont(be_plain_font); 83 84 msg = new BMessage(MSG_SET_BEHAVIOUR); 85 msg->AddInt32(AR_BEHAVIOUR, B_NORMAL_MOUSE); 86 tmpi = new BMenuItem("Normal", msg); 87 tmpi->SetMarked(tv->fNormalMM == B_NORMAL_MOUSE); 88 tmpm->AddItem(tmpi); 89 90 msg = new BMessage(MSG_SET_BEHAVIOUR); 91 msg->AddInt32(AR_BEHAVIOUR, B_FOCUS_FOLLOWS_MOUSE); 92 tmpi = new BMenuItem("Focus follows mouse", msg); 93 tmpi->SetMarked(tv->fNormalMM == B_FOCUS_FOLLOWS_MOUSE); 94 tmpm->AddItem(tmpi); 95 96 msg = new BMessage(MSG_SET_BEHAVIOUR); 97 msg->AddInt32(AR_BEHAVIOUR, B_WARP_FOCUS_FOLLOWS_MOUSE); 98 tmpi = new BMenuItem("Warping (ffm)", msg); 99 tmpi->SetMarked(tv->fNormalMM == (mode_mouse)B_WARP_FOCUS_FOLLOWS_MOUSE); 100 tmpm->AddItem(tmpi); 101 102 msg = new BMessage(MSG_SET_BEHAVIOUR); 103 msg->AddInt32(AR_BEHAVIOUR, B_INSTANT_WARP_FOCUS_FOLLOWS_MOUSE); 104 tmpi = new BMenuItem("Instant warping (ffm)", msg); 105 tmpi->SetMarked(tv->fNormalMM == (mode_mouse)B_INSTANT_WARP_FOCUS_FOLLOWS_MOUSE); 106 tmpm->AddItem(tmpi); 107 108 tmpm->SetTargetForItems(tv); 109 BMenuItem *behavm = new BMenuItem(tmpm); 110 AddItem(behavm); 111 112 113 tmpm = new BMenu("Delay"); 114 tmpm->SetFont(be_plain_font); 115 116 msg = new BMessage(MSG_SET_DELAY); 117 msg->AddInt64(AR_DELAY, 100000LL); 118 tmpi = new BMenuItem("0.1 s", msg); 119 tmpi->SetMarked(tv->raise_delay == 100000LL); 120 tmpm->AddItem(tmpi); 121 122 msg = new BMessage(MSG_SET_DELAY); 123 msg->AddInt64(AR_DELAY, 200000LL); 124 tmpi = new BMenuItem("0.2 s", msg); 125 tmpi->SetMarked(tv->raise_delay == 200000LL); 126 tmpm->AddItem(tmpi); 127 128 msg = new BMessage(MSG_SET_DELAY); 129 msg->AddInt64(AR_DELAY, 500000LL); 130 tmpi = new BMenuItem("0.5 s", msg); 131 tmpi->SetMarked(tv->raise_delay == 500000LL); 132 tmpm->AddItem(tmpi); 133 134 msg = new BMessage(MSG_SET_DELAY); 135 msg->AddInt64(AR_DELAY, 1000000LL); 136 tmpi = new BMenuItem("1 s", msg); 137 tmpi->SetMarked(tv->raise_delay == 1000000LL); 138 tmpm->AddItem(tmpi); 139 140 msg = new BMessage(MSG_SET_DELAY); 141 msg->AddInt64(AR_DELAY, 2000000LL); 142 tmpi = new BMenuItem("2 s", msg); 143 tmpi->SetMarked(tv->raise_delay == 2000000LL); 144 tmpm->AddItem(tmpi); 145 146 msg = new BMessage(MSG_SET_DELAY); 147 msg->AddInt64(AR_DELAY, 3000000LL); 148 tmpi = new BMenuItem("3 s", msg); 149 tmpi->SetMarked(tv->raise_delay == 3000000LL); 150 tmpm->AddItem(tmpi); 151 152 msg = new BMessage(MSG_SET_DELAY); 153 msg->AddInt64(AR_DELAY, 4000000LL); 154 tmpi = new BMenuItem("4 s", msg); 155 tmpi->SetMarked(tv->raise_delay == 4000000LL); 156 tmpm->AddItem(tmpi); 157 158 msg = new BMessage(MSG_SET_DELAY); 159 msg->AddInt64(AR_DELAY, 5000000LL); 160 tmpi = new BMenuItem("5 s", msg); 161 tmpi->SetMarked(tv->raise_delay == 5000000LL); 162 tmpm->AddItem(tmpi); 163 164 tmpm->SetTargetForItems(tv); 165 BMenuItem *delaym = new BMenuItem(tmpm); 166 delaym->SetEnabled(s->Active()); 167 168 AddItem(delaym); 169 170 AddSeparatorItem(); 171 // AddItem(new BMenuItem("Settings...", new BMessage(OPEN_SETTINGS))); 172 173 AddItem(new BMenuItem("About "APP_NAME B_UTF8_ELLIPSIS, 174 new BMessage(B_ABOUT_REQUESTED))); 175 AddItem(new BMenuItem("Remove from tray", new BMessage(REMOVE_FROM_TRAY))); 176 177 SetTargetForItems(tv); 178 SetAsyncAutoDestruct(true); 179 } 180 181 ConfigMenu::~ConfigMenu() {} 182 183 //************************************************ 184 185 TrayView::TrayView() 186 :BView(BRect(0, 0, B_MINI_ICON, B_MINI_ICON -1), "AutoRaise", B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW){ 187 _init(); //Initialization common to both constructors 188 } 189 190 //Rehydratable constructor 191 TrayView::TrayView(BMessage *mdArchive):BView(mdArchive){ 192 _init(); //As above 193 } 194 195 void TrayView::GetPreferredSize(float *w, float *h) 196 { 197 *w = B_MINI_ICON; 198 *h = B_MINI_ICON - 1; 199 } 200 201 void TrayView::_init() 202 { 203 thread_info ti; 204 status_t err; 205 206 watching = false; 207 _settings = new AutoRaiseSettings; 208 209 _appPath = _settings->AppPath(); 210 211 raise_delay = _settings->Delay(); 212 current_window = 0; 213 polling_delay = 100000; 214 fPollerSem = create_sem(0, "AutoRaise poller sync"); 215 last_raiser_thread = 0; 216 fNormalMM = mouse_mode(); 217 218 _activeIcon = NULL; 219 _inactiveIcon = NULL; 220 221 get_thread_info(find_thread(NULL), &ti); 222 fDeskbarTeam = ti.team; 223 224 #ifndef USE_DANO_HACK 225 resume_thread(poller_thread = spawn_thread(poller, "AutoRaise desktop " 226 "poller", B_NORMAL_PRIORITY, (void *)this)); 227 #endif 228 229 //determine paths to icon files based on app path in settings file 230 231 BResources res; 232 BFile theapp(&_appPath, B_READ_ONLY); 233 if ((err = res.SetTo(&theapp)) != B_OK) { 234 235 printf("Unable to find the app to get the resources !!!\n"); 236 // removeFromDeskbar(NULL); 237 // delete _settings; 238 // return; 239 } 240 241 size_t bmsz; 242 char *p; 243 244 p = (char *)res.LoadResource('MICN', ACTIVE_ICON, &bmsz); 245 _activeIcon = new BBitmap(BRect(0, 0, B_MINI_ICON-1, B_MINI_ICON -1), 246 B_CMAP8); 247 if (!p) 248 puts("ERROR loading active icon"); 249 else 250 _activeIcon->SetBits(p, B_MINI_ICON*B_MINI_ICON, 0, B_CMAP8); 251 252 p = (char *)res.LoadResource('MICN', INACTIVE_ICON, &bmsz); 253 _inactiveIcon = new BBitmap(BRect(0, 0, B_MINI_ICON-1, B_MINI_ICON -1), 254 B_CMAP8); 255 if (!p) 256 puts("ERROR loading inactive icon"); 257 else 258 _inactiveIcon->SetBits(p, B_MINI_ICON*B_MINI_ICON, 0, B_CMAP8); 259 260 261 SetDrawingMode(B_OP_ALPHA); 262 SetFlags(Flags() | B_WILL_DRAW); 263 264 // begin watching if we want 265 // (doesn't work here, better do it in AttachedToWindow()) 266 } 267 268 TrayView::~TrayView(){ 269 status_t ret; 270 271 if (watching) { 272 #ifdef USE_DANO_HACK 273 be_roster->StopWatching(this); 274 #else 275 // acquire_sem(fPollerSem); 276 #endif 277 set_mouse_mode(fNormalMM); 278 watching = false; 279 } 280 delete_sem(fPollerSem); 281 #ifndef USE_DANO_HACK 282 wait_for_thread(poller_thread, &ret); 283 #endif 284 if (_activeIcon) delete _activeIcon; 285 if (_inactiveIcon) delete _inactiveIcon; 286 if (_settings) delete _settings; 287 288 return; 289 } 290 291 //Dehydrate into a message (called by the DeskBar) 292 status_t TrayView::Archive(BMessage *data, bool deep) const { 293 // BEntry appentry(&_appPath, true); 294 // BPath appPath(&appentry); 295 status_t error=BView::Archive(data, deep); 296 data->AddString("add_on", APP_SIG); 297 // data->AddFlat("_appPath", (BFlattenable *) &_appPath); 298 data->AddRef("_appPath", &_appPath); 299 300 return error; 301 } 302 303 //Rehydrate the View from a given message (called by the DeskBar) 304 TrayView *TrayView::Instantiate(BMessage *data) { 305 306 if (!validate_instantiation(data, "TrayView")) 307 { 308 return NULL; 309 } 310 311 return (new TrayView(data)); 312 } 313 314 void TrayView::AttachedToWindow() { 315 if(Parent()) 316 SetViewColor(Parent()->ViewColor()); 317 if (_settings->Active()) { 318 fNormalMM = mouse_mode(); 319 set_mouse_mode(B_FOCUS_FOLLOWS_MOUSE); 320 #ifdef USE_DANO_HACK 321 be_roster->StartWatching(this, B_REQUEST_WINDOW_ACTIVATED); 322 #else 323 release_sem(fPollerSem); 324 #endif 325 watching = true; 326 } 327 } 328 329 void TrayView::Draw(BRect updaterect) { 330 BRect bnds(Bounds()); 331 332 if (Parent()) SetHighColor(Parent()->ViewColor()); 333 else SetHighColor(189, 186, 189, 255); 334 FillRect(bnds); 335 336 if (_settings->Active()) 337 { 338 if (_activeIcon) DrawBitmap(_activeIcon); 339 } 340 else 341 { 342 if (_inactiveIcon) DrawBitmap(_inactiveIcon); 343 } 344 } 345 346 void TrayView::MouseDown(BPoint where) { 347 BWindow *window = Window(); /*To handle the MouseDown message*/ 348 if (!window) /*Check for proper instantiation*/ 349 return; 350 351 BMessage *mouseMsg = window->CurrentMessage(); 352 if (!mouseMsg) /*Check for existence*/ 353 return; 354 355 if (mouseMsg->what == B_MOUSE_DOWN) { 356 /*Variables for storing the button pressed / modifying key*/ 357 uint32 buttons = 0; 358 uint32 modifiers = 0; 359 360 /*Get the button pressed*/ 361 mouseMsg->FindInt32("buttons", (int32 *) &buttons); 362 /*Get modifier key (if any)*/ 363 mouseMsg->FindInt32("modifiers", (int32 *) &modifiers); 364 365 /*Now perform action*/ 366 switch(buttons) { 367 case B_PRIMARY_MOUSE_BUTTON: 368 { 369 SetActive(!_settings->Active()); 370 371 break; 372 } 373 case B_SECONDARY_MOUSE_BUTTON: 374 { 375 ConvertToScreen(&where); 376 377 //menu will delete itself (see constructor of ConfigMenu), 378 //so all we're concerned about is calling Go() asynchronously 379 ConfigMenu *menu = new ConfigMenu(this, false); 380 menu->Go(where, true, true, ConvertToScreen(Bounds()), true); 381 382 break; 383 } 384 } 385 } 386 } 387 388 389 int32 fronter(void *arg) 390 { 391 TrayView *tv = (TrayView *)arg; 392 int32 tok = tv->current_window; 393 int32 ws = current_workspace(); 394 sem_id sem = tv->fPollerSem; 395 396 snooze(tv->raise_delay); 397 398 #ifndef USE_DANO_HACK 399 if (acquire_sem(sem) != B_OK) 400 return B_OK; // this really needs a better locking model... 401 #endif 402 if (ws != current_workspace()) 403 goto end; // don't touch windows if we changed workspace 404 if (tv->last_raiser_thread != find_thread(NULL)) 405 goto end; // seems a newer one has been spawn, exit 406 PRINT(("tok = %ld cw = %ld\n", tok, tv->current_window)); 407 if (tok == tv->current_window) { 408 bool doZoom = false; 409 BRect zoomRect(0.0f, 0.0f, 10.0f, 10.0f); 410 do_window_action(tok, B_BRING_TO_FRONT, zoomRect, doZoom); 411 } 412 413 end: 414 release_sem(sem); 415 return B_OK; 416 } 417 418 #ifndef USE_DANO_HACK 419 420 int32 poller(void *arg) 421 { 422 TrayView *tv = (TrayView *)arg; 423 volatile int32 tok = tv->current_window; 424 int32 *tl = NULL; 425 int32 i, tlc; 426 window_info *wi = NULL; 427 428 int pass=0; 429 BPoint mouse; 430 uint32 buttons; 431 432 while (acquire_sem(tv->fPollerSem) == B_OK) { 433 release_sem(tv->fPollerSem); 434 pass++; 435 BLooper *l = tv->Looper(); 436 if (!l || l->LockWithTimeout(500000) != B_OK) 437 continue; 438 tv->GetMouse(&mouse, &buttons); 439 tv->ConvertToScreen(&mouse); 440 tv->Looper()->Unlock(); 441 if (buttons) // we don't want to interfere when the user is moving a window or something... 442 goto zzz; 443 444 tl = get_token_list(-1, &tlc); 445 for (i=0; i<tlc; i++) { 446 wi = get_window_info(tl[i]); 447 if (wi) { 448 if (wi->layer < 3) // we hit the desktop or a window not on this WS 449 goto zzz; 450 if ((wi->window_left > wi->window_right) || (wi->window_top > wi->window_bottom)) 451 goto zzz; // invalid window ? 452 /* 453 printf("if (!%s && (%li, %li)isin(%li)(%li, %li, %li, %li) && (%li != %li) ", wi->is_mini?"true":"false", 454 (long)mouse.x, (long)mouse.y, i, wi->window_left, wi->window_right, wi->window_top, wi->window_bottom, wi->id, tok); 455 */ 456 457 458 if ((!wi->is_mini) 459 && (((long)mouse.x) > wi->window_left) && (((long)mouse.x) < wi->window_right) 460 && (((long)mouse.y) > wi->window_top) && (((long)mouse.y) < wi->window_bottom)) { 461 //((tv->_settings->Mode() != Mode_DeskbarOver) || (wi->team == tv->fDeskbarTeam)) 462 463 if ((tv->_settings->Mode() == Mode_All) && (wi->id == tv->current_window)) 464 goto zzz; // already raised 465 466 if ((tv->_settings->Mode() == Mode_All) || (wi->team == tv->fDeskbarTeam)) { 467 tv->current_window = wi->id; 468 tok = wi->id; 469 resume_thread(tv->last_raiser_thread = spawn_thread(fronter, "fronter", B_NORMAL_PRIORITY, (void *)tv)); 470 goto zzz; 471 } else if (tv->_settings->Mode() == Mode_DeskbarTouch) // give up, before we find Deskbar under it 472 goto zzz; 473 } 474 free(wi); 475 wi=NULL; 476 } else 477 goto zzz; 478 } 479 zzz: 480 // puts(""); 481 if (wi) free(wi); 482 wi = NULL; 483 if (tl) free(tl); 484 tl = NULL; 485 snooze(tv->polling_delay); 486 } 487 return B_OK; 488 } 489 490 #endif 491 492 void TrayView::MessageReceived(BMessage* message) 493 { 494 BMessenger msgr; 495 496 BAlert *alert; 497 bigtime_t delay; 498 int32 mode; 499 500 switch(message->what) 501 { 502 case MSG_TOOGLE_ACTIVE: 503 SetActive(!_settings->Active()); 504 break; 505 case MSG_SET_ACTIVE: 506 SetActive(true); 507 break; 508 case MSG_SET_INACTIVE: 509 SetActive(false); 510 break; 511 case MSG_SET_DELAY: 512 delay = DEFAULT_DELAY; 513 message->FindInt64(AR_DELAY, &delay); 514 raise_delay = delay; 515 _settings->SetDelay(delay); 516 break; 517 case MSG_SET_MODE: 518 mode = Mode_All; 519 message->FindInt32(AR_MODE, &mode); 520 _settings->SetMode(mode); 521 break; 522 case MSG_SET_BEHAVIOUR: 523 { 524 message->FindInt32(AR_BEHAVIOUR, &mode); 525 bool wasactive = _settings->Active(); 526 if (wasactive) 527 SetActive(false); 528 fNormalMM = (mode_mouse)mode; 529 set_mouse_mode(fNormalMM); 530 if (wasactive) 531 SetActive(true); 532 break; 533 } 534 case REMOVE_FROM_TRAY: 535 { 536 thread_id tid = spawn_thread(removeFromDeskbar, "RemoveFromDeskbar", B_NORMAL_PRIORITY, NULL); 537 if (tid) resume_thread(tid); 538 539 break; 540 } 541 case B_ABOUT_REQUESTED: 542 alert = new BAlert("about box", "AutoRaise, (c) 2002, mmu_man\nEnjoy :-)", "OK", NULL, NULL, 543 B_WIDTH_AS_USUAL, B_OFFSET_SPACING, B_INFO_ALERT); 544 alert->SetShortcut(0, B_ENTER); 545 alert->Go(NULL); // use asynchronous version 546 break; 547 case OPEN_SETTINGS: 548 549 break; 550 #ifdef USE_DANO_HACK 551 case B_SOME_WINDOW_ACTIVATED: 552 // printf("Window Activated\n"); 553 // message->PrintToStream(); 554 BPoint mouse; 555 uint32 buttons; 556 GetMouse(&mouse, &buttons); 557 if (buttons) 558 break; 559 if (message->FindMessenger("be:window", &msgr) < B_OK) 560 puts("BMsgr ERROR"); 561 else { 562 bool doZoom = false; 563 BRect zoomRect(0.0f, 0.0f, 0.0f, 0.0f); 564 port_id pi = msgr.fPort; 565 // printf("port:%li (%lx)\n", pi, pi); 566 567 int32 *tl, tlc; 568 tl = get_token_list(msgr.Team(), &tlc); 569 // printf("tokens (team %li): (%li) ", msgr.Team(), tlc); 570 for (tlc; tlc; tlc--) { 571 // printf("%li ", tl[tlc-1]); 572 window_info *wi = get_window_info(tl[tlc-1]); 573 if (wi) { 574 if (wi->client_port == pi) { 575 if ((wi->layer < 3) // we hit the desktop or a window not on this WS 576 || (wi->window_left > wi->window_right) || (wi->window_top > wi->window_bottom) 577 || (wi->is_mini) 578 || (/*(_settings->Mode() == Mode_All) && */(wi->id == current_window))) { 579 // already raised 580 free(wi); 581 break; 582 } 583 584 if ((_settings->Mode() == Mode_All) || (wi->team == fDeskbarTeam)) { 585 PRINT(("raising wi=%li, cp=%ld, pi=%ld team=%ld DBteam=%ld\n", wi->id, wi->client_port, pi, wi->team, fDeskbarTeam)); 586 current_window = wi->id; 587 int32 tok = wi->id; 588 resume_thread(last_raiser_thread = spawn_thread(fronter, "fronter", B_NORMAL_PRIORITY, (void *)this)); 589 } else { 590 current_window = wi->id; 591 } 592 } 593 free(wi); 594 } 595 } 596 // puts(""); 597 free(tl); 598 } 599 break; 600 #endif 601 default: 602 BView::MessageReceived(message); 603 } 604 } 605 606 AutoRaiseSettings *TrayView::Settings() const 607 { 608 return _settings; 609 } 610 611 void TrayView::SetActive(bool st) 612 { 613 _settings->SetActive(st); 614 if (_settings->Active()) 615 { 616 if (!watching) { 617 fNormalMM = mouse_mode(); 618 set_mouse_mode(B_FOCUS_FOLLOWS_MOUSE); 619 #ifdef USE_DANO_HACK 620 be_roster->StartWatching(this, B_REQUEST_WINDOW_ACTIVATED); 621 #else 622 release_sem(fPollerSem); 623 #endif 624 watching = true; 625 } 626 } 627 else 628 { 629 if (watching) { 630 #ifdef USE_DANO_HACK 631 be_roster->StopWatching(this); 632 #else 633 acquire_sem(fPollerSem); 634 #endif 635 set_mouse_mode(fNormalMM); 636 watching = false; 637 } 638 } 639 Invalidate(); 640 } 641 642