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