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