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