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