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
instantiate_deskbar_item(void)17 extern "C" _EXPORT BView *instantiate_deskbar_item(void)
18 {
19 puts("Instanciating AutoRaise TrayView...");
20 return (new TrayView);
21 }
22
23
removeFromDeskbar(void *)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
our_image(image_info & image)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
ConfigMenu(TrayView * tv,bool useMag)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
~ConfigMenu()196 ConfigMenu::~ConfigMenu() {}
197
198 //************************************************
199
TrayView()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
TrayView(BMessage * mdArchive)206 TrayView::TrayView(BMessage *mdArchive):BView(mdArchive){
207 _init(); //As above
208 }
209
GetPreferredSize(float * w,float * h)210 void TrayView::GetPreferredSize(float *w, float *h)
211 {
212 *w = B_MINI_ICON;
213 *h = B_MINI_ICON - 1;
214 }
215
_init()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
~TrayView()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)
Archive(BMessage * data,bool deep) const313 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)
Instantiate(BMessage * data)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
AttachedToWindow()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
Draw(BRect updaterect)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
MouseDown(BPoint where)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
fronter(void * arg)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
poller(void * arg)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
MessageReceived(BMessage * message)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
Settings() const575 AutoRaiseSettings *TrayView::Settings() const
576 {
577 return _settings;
578 }
579
SetActive(bool st)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