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_MOUSE); 99 tmpi = new BMenuItem("Warping (ffm)", msg); 100 tmpi->SetMarked(tv->fNormalMM == B_WARP_MOUSE); 101 tmpm->AddItem(tmpi); 102 103 msg = new BMessage(MSG_SET_BEHAVIOUR); 104 msg->AddInt32(AR_BEHAVIOUR, B_INSTANT_WARP_MOUSE); 105 tmpi = new BMenuItem("Instant warping (ffm)", msg); 106 tmpi->SetMarked(tv->fNormalMM == B_INSTANT_WARP_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, 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 poller", B_NORMAL_PRIORITY, (void *)this)); 226 #endif 227 228 //determine paths to icon files based on app path in settings file 229 230 BResources res; 231 BFile theapp(&_appPath, B_READ_ONLY); 232 if ((err = res.SetTo(&theapp)) != B_OK) { 233 234 printf("Unable to find the app to get the resources !!!\n"); 235 // removeFromDeskbar(NULL); 236 // delete _settings; 237 // return; 238 } 239 240 size_t bmsz; 241 char *p; 242 243 p = (char *)res.LoadResource('MICN', ACTIVE_ICON, &bmsz); 244 _activeIcon = new BBitmap(BRect(0, 0, B_MINI_ICON-1, B_MINI_ICON -1), B_CMAP8); 245 if (!p) 246 puts("ERROR loading active icon"); 247 else 248 _activeIcon->SetBits(p, B_MINI_ICON*B_MINI_ICON, 0, B_CMAP8); 249 250 p = (char *)res.LoadResource('MICN', INACTIVE_ICON, &bmsz); 251 _inactiveIcon = new BBitmap(BRect(0, 0, B_MINI_ICON-1, B_MINI_ICON -1), B_CMAP8); 252 if (!p) 253 puts("ERROR loading inactive icon"); 254 else 255 _inactiveIcon->SetBits(p, B_MINI_ICON*B_MINI_ICON, 0, B_CMAP8); 256 257 258 SetDrawingMode(B_OP_ALPHA); 259 SetFlags(Flags() | B_WILL_DRAW); 260 261 // begin watching if we want 262 // (doesn't work here, better do it in AttachedToWindow()) 263 } 264 265 TrayView::~TrayView(){ 266 status_t ret; 267 268 if (watching) { 269 #ifdef USE_DANO_HACK 270 be_roster->StopWatching(this); 271 #else 272 // acquire_sem(fPollerSem); 273 #endif 274 set_mouse_mode(fNormalMM); 275 watching = false; 276 } 277 delete_sem(fPollerSem); 278 #ifndef USE_DANO_HACK 279 wait_for_thread(poller_thread, &ret); 280 #endif 281 if (_activeIcon) delete _activeIcon; 282 if (_inactiveIcon) delete _inactiveIcon; 283 if (_settings) delete _settings; 284 285 return; 286 } 287 288 //Dehydrate into a message (called by the DeskBar) 289 status_t TrayView::Archive(BMessage *data, bool deep = true) const { 290 // BEntry appentry(&_appPath, true); 291 // BPath appPath(&appentry); 292 status_t error=BView::Archive(data, deep); 293 data->AddString("add_on", APP_SIG); 294 // data->AddFlat("_appPath", (BFlattenable *) &_appPath); 295 data->AddRef("_appPath", &_appPath); 296 297 return B_NO_ERROR; 298 } 299 300 //Rehydrate the View from a given message (called by the DeskBar) 301 TrayView *TrayView::Instantiate(BMessage *data) { 302 303 if (!validate_instantiation(data, "TrayView")) 304 { 305 return NULL; 306 } 307 308 return (new TrayView(data)); 309 } 310 311 void TrayView::AttachedToWindow() { 312 if(Parent()) 313 SetViewColor(Parent()->ViewColor()); 314 if (_settings->Active()) { 315 fNormalMM = mouse_mode(); 316 set_mouse_mode(B_FOCUS_FOLLOWS_MOUSE); 317 #ifdef USE_DANO_HACK 318 be_roster->StartWatching(this, B_REQUEST_WINDOW_ACTIVATED); 319 #else 320 release_sem(fPollerSem); 321 #endif 322 watching = true; 323 } 324 } 325 326 void TrayView::Draw(BRect updaterect) { 327 BRect bnds(Bounds()); 328 329 if (Parent()) SetHighColor(Parent()->ViewColor()); 330 else SetHighColor(189, 186, 189, 255); 331 FillRect(bnds); 332 333 if (_settings->Active()) 334 { 335 if (_activeIcon) DrawBitmap(_activeIcon); 336 } 337 else 338 { 339 if (_inactiveIcon) DrawBitmap(_inactiveIcon); 340 } 341 } 342 343 void TrayView::MouseDown(BPoint where) { 344 BWindow *window = Window(); /*To handle the MouseDown message*/ 345 if (!window) /*Check for proper instantiation*/ 346 return; 347 348 BMessage *mouseMsg = window->CurrentMessage(); 349 if (!mouseMsg) /*Check for existence*/ 350 return; 351 352 if (mouseMsg->what == B_MOUSE_DOWN) { 353 /*Variables for storing the button pressed / modifying key*/ 354 uint32 buttons = 0; 355 uint32 modifiers = 0; 356 357 /*Get the button pressed*/ 358 mouseMsg->FindInt32("buttons", (int32 *) &buttons); 359 /*Get modifier key (if any)*/ 360 mouseMsg->FindInt32("modifiers", (int32 *) &modifiers); 361 362 /*Now perform action*/ 363 switch(buttons) { 364 case B_PRIMARY_MOUSE_BUTTON: 365 { 366 SetActive(!_settings->Active()); 367 368 break; 369 } 370 case B_SECONDARY_MOUSE_BUTTON: 371 { 372 ConvertToScreen(&where); 373 374 //menu will delete itself (see constructor of ConfigMenu), 375 //so all we're concerned about is calling Go() asynchronously 376 ConfigMenu *menu = new ConfigMenu(this, false); 377 menu->Go(where, true, true, ConvertToScreen(Bounds()), true); 378 379 break; 380 } 381 } 382 } 383 } 384 385 386 int32 fronter(void *arg) 387 { 388 TrayView *tv = (TrayView *)arg; 389 int32 tok = tv->current_window; 390 int32 ws = current_workspace(); 391 sem_id sem = tv->fPollerSem; 392 int32 *tl, tlc; 393 window_info *wi; 394 395 snooze(tv->raise_delay); 396 397 #ifndef USE_DANO_HACK 398 if (acquire_sem(sem) != B_OK) 399 return B_OK; // this really needs a better locking model... 400 #endif 401 if (ws != current_workspace()) 402 goto end; // don't touch windows if we changed workspace 403 if (tv->last_raiser_thread != find_thread(NULL)) 404 goto end; // seems a newer one has been spawn, exit 405 PRINT(("tok = %ld cw = %ld\n", tok, tv->current_window)); 406 if (tok == tv->current_window) { 407 bool doZoom = false; 408 BRect zoomRect(0.0f, 0.0f, 10.0f, 10.0f); 409 do_window_action(tok, B_BRING_TO_FRONT, zoomRect, doZoom); 410 } 411 412 end: 413 release_sem(sem); 414 return B_OK; 415 } 416 417 #ifndef USE_DANO_HACK 418 419 int32 poller(void *arg) 420 { 421 TrayView *tv = (TrayView *)arg; 422 volatile int32 tok = tv->current_window; 423 int32 *tl = NULL; 424 int32 i, tlc; 425 window_info *wi = NULL; 426 427 int pass=0; 428 BPoint mouse; 429 uint32 buttons; 430 431 while (acquire_sem(tv->fPollerSem) == B_OK) { 432 release_sem(tv->fPollerSem); 433 pass++; 434 BLooper *l = tv->Looper(); 435 if (!l || l->LockWithTimeout(500000) != B_OK) 436 continue; 437 tv->GetMouse(&mouse, &buttons); 438 tv->ConvertToScreen(&mouse); 439 tv->Looper()->Unlock(); 440 if (buttons) // we don't want to interfere when the user is moving a window or something... 441 goto zzz; 442 443 tl = get_token_list(-1, &tlc); 444 for (i=0; i<tlc; i++) { 445 wi = get_window_info(tl[i]); 446 if (wi) { 447 if (wi->layer < 3) // we hit the desktop or a window not on this WS 448 goto zzz; 449 if ((wi->window_left > wi->window_right) || (wi->window_top > wi->window_bottom)) 450 goto zzz; // invalid window ? 451 /* 452 printf("if (!%s && (%li, %li)isin(%li)(%li, %li, %li, %li) && (%li != %li) ", wi->is_mini?"true":"false", 453 (long)mouse.x, (long)mouse.y, i, wi->window_left, wi->window_right, wi->window_top, wi->window_bottom, wi->id, tok); 454 */ 455 456 457 if ((!wi->is_mini) 458 && (((long)mouse.x) > wi->window_left) && (((long)mouse.x) < wi->window_right) 459 && (((long)mouse.y) > wi->window_top) && (((long)mouse.y) < wi->window_bottom)) { 460 //((tv->_settings->Mode() != Mode_DeskbarOver) || (wi->team == tv->fDeskbarTeam)) 461 462 if ((tv->_settings->Mode() == Mode_All) && (wi->id == tv->current_window)) 463 goto zzz; // already raised 464 465 if ((tv->_settings->Mode() == Mode_All) || (wi->team == tv->fDeskbarTeam)) { 466 tv->current_window = wi->id; 467 tok = wi->id; 468 resume_thread(tv->last_raiser_thread = spawn_thread(fronter, "fronter", B_NORMAL_PRIORITY, (void *)tv)); 469 goto zzz; 470 } else if (tv->_settings->Mode() == Mode_DeskbarTouch) // give up, before we find Deskbar under it 471 goto zzz; 472 } 473 free(wi); 474 wi=NULL; 475 } else 476 goto zzz; 477 } 478 zzz: 479 // puts(""); 480 if (wi) free(wi); 481 wi = NULL; 482 if (tl) free(tl); 483 tl = NULL; 484 snooze(tv->polling_delay); 485 } 486 return B_OK; 487 } 488 489 #endif 490 491 void TrayView::MessageReceived(BMessage* message) 492 { 493 BMessenger msgr; 494 int32 *tl, tlc; 495 port_id pi; 496 int32 tok; 497 window_info *wi; 498 499 BAlert *alert; 500 bigtime_t delay; 501 int32 mode; 502 bool wasactive; 503 BPoint mouse; 504 uint32 buttons; 505 506 switch(message->what) 507 { 508 case MSG_TOOGLE_ACTIVE: 509 SetActive(!_settings->Active()); 510 break; 511 case MSG_SET_ACTIVE: 512 SetActive(true); 513 break; 514 case MSG_SET_INACTIVE: 515 SetActive(false); 516 break; 517 case MSG_SET_DELAY: 518 delay = DEFAULT_DELAY; 519 message->FindInt64(AR_DELAY, &delay); 520 raise_delay = delay; 521 _settings->SetDelay(delay); 522 break; 523 case MSG_SET_MODE: 524 mode = Mode_All; 525 message->FindInt32(AR_MODE, &mode); 526 _settings->SetMode(mode); 527 break; 528 case MSG_SET_BEHAVIOUR: 529 message->FindInt32(AR_BEHAVIOUR, &mode); 530 wasactive = _settings->Active(); 531 if (wasactive) 532 SetActive(false); 533 fNormalMM = (mode_mouse)mode; 534 set_mouse_mode(fNormalMM); 535 if (wasactive) 536 SetActive(true); 537 break; 538 case REMOVE_FROM_TRAY: 539 { 540 thread_id tid = spawn_thread(removeFromDeskbar, "RemoveFromDeskbar", B_NORMAL_PRIORITY, NULL); 541 if (tid) resume_thread(tid); 542 543 break; 544 } 545 case B_ABOUT_REQUESTED: 546 alert = new BAlert("about box", "AutoRaise, (c) 2002, mmu_man\nEnjoy :-)", "Ok", NULL, NULL, 547 B_WIDTH_AS_USUAL, B_OFFSET_SPACING, B_INFO_ALERT); 548 alert->SetShortcut(0, B_ENTER); 549 alert->Go(NULL); // use asynchronous version 550 break; 551 case OPEN_SETTINGS: 552 553 break; 554 #ifdef USE_DANO_HACK 555 case B_SOME_WINDOW_ACTIVATED: 556 // printf("Window Activated\n"); 557 // message->PrintToStream(); 558 GetMouse(&mouse, &buttons); 559 if (buttons) 560 break; 561 if (message->FindMessenger("be:window", &msgr) < B_OK) 562 puts("BMsgr ERROR"); 563 else { 564 bool doZoom = false; 565 BRect zoomRect(0.0f, 0.0f, 0.0f, 0.0f); 566 pi = msgr.fPort; 567 // printf("port:%li (%lx)\n", pi, pi); 568 569 tl = get_token_list(msgr.Team(), &tlc); 570 // printf("tokens (team %li): (%li) ", msgr.Team(), tlc); 571 for (tlc; tlc; tlc--) { 572 // printf("%li ", tl[tlc-1]); 573 wi = get_window_info(tl[tlc-1]); 574 if (wi) { 575 if (wi->client_port == pi) { 576 if ((wi->layer < 3) // we hit the desktop or a window not on this WS 577 || (wi->window_left > wi->window_right) || (wi->window_top > wi->window_bottom) 578 || (wi->is_mini) 579 || (/*(_settings->Mode() == Mode_All) && */(wi->id == current_window))) { 580 // already raised 581 free(wi); 582 break; 583 } 584 585 if ((_settings->Mode() == Mode_All) || (wi->team == fDeskbarTeam)) { 586 PRINT(("raising wi=%li, cp=%ld, pi=%ld team=%ld DBteam=%ld\n", wi->id, wi->client_port, pi, wi->team, fDeskbarTeam)); 587 current_window = wi->id; 588 tok = wi->id; 589 resume_thread(last_raiser_thread = spawn_thread(fronter, "fronter", B_NORMAL_PRIORITY, (void *)this)); 590 } else { 591 current_window = wi->id; 592 } 593 } 594 free(wi); 595 } 596 } 597 // puts(""); 598 free(tl); 599 } 600 break; 601 #endif 602 default: 603 BView::MessageReceived(message); 604 } 605 } 606 607 AutoRaiseSettings *TrayView::Settings() const 608 { 609 return _settings; 610 } 611 612 void TrayView::SetActive(bool st) 613 { 614 _settings->SetActive(st); 615 if (_settings->Active()) 616 { 617 if (!watching) { 618 fNormalMM = mouse_mode(); 619 set_mouse_mode(B_FOCUS_FOLLOWS_MOUSE); 620 #ifdef USE_DANO_HACK 621 be_roster->StartWatching(this, B_REQUEST_WINDOW_ACTIVATED); 622 #else 623 release_sem(fPollerSem); 624 #endif 625 watching = true; 626 } 627 } 628 else 629 { 630 if (watching) { 631 #ifdef USE_DANO_HACK 632 be_roster->StopWatching(this); 633 #else 634 acquire_sem(fPollerSem); 635 #endif 636 set_mouse_mode(fNormalMM); 637 watching = false; 638 } 639 } 640 Invalidate(); 641 } 642 643