1 /* vim:tabstop=4:expandtab:shiftwidth=4
2 *
3 * Idesk -- XDesktopContainer.cpp
4 *
5 * Copyright (c) 2002, Chris (nikon) (nikon@sc.rr.com)
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 *
11 * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * Neither the name of the <ORGANIZATION> nor the names of its
19 * contributors may be used to endorse or promote products derived from
20 * this software without specific prior written permission.
21 *
22 * (See the included file COPYING / BSD )
23 */
24
25 #include "XDesktopContainer.h"
26 #include "XIconWithShadow.h"
27 #include "XImlib2Background.h"
28 #include "Database.h"
29
30
31 #include <X11/keysym.h>
32 #ifdef HAVE_STARTUP_NOTIFICATION
33 #include <libsn/sn.h>
34 #endif /* HAVE_STARTUP_NOTIFICATION */
35
36 XDesktopContainer *xcontainer;
37
XDesktopContainer(AbstractApp * a)38 XDesktopContainer::XDesktopContainer(AbstractApp * a) : DesktopContainer(a)
39 {
40 xcontainer=this;
41 initXWin();
42 initImlib();
43 }
44
run()45 void XDesktopContainer::run()
46 {
47 times[0] = 0; times[1] = 0; times[2] = 0;
48 numClicks[0] = 0; numClicks[1] = 0; numClicks[2] = 0;
49 configure();
50 create();
51 loadIcons();
52 arrangeIcons();
53
54 eventLoop();
55 }
56
~XDesktopContainer()57 XDesktopContainer::~XDesktopContainer()
58 {
59 destroy();
60 XCloseDisplay(display);
61 }
62
create()63 void XDesktopContainer::create()
64 {
65 getRootImage();
66 }
67
destroy()68 void XDesktopContainer::destroy()
69 {
70
71 vector<AbstractIcon *>::reverse_iterator rIt = iconList.rbegin();
72 for(; rIt != iconList.rend(); rIt++)
73 delete *rIt;
74
75 iconList.clear();
76
77 delete config;
78 delete actionConfig;
79 delete bg;
80
81 XFlush(display);
82 }
83
initXWin()84 void XDesktopContainer::initXWin()
85 {
86
87 display = XOpenDisplay(NULL);
88
89 if (!display){
90 cout << "Display is null!!\n";
91 _exit(1);
92 }
93
94 char *name = DisplayString(display);
95 int screen = DefaultScreen(display);
96 rootWindow = RootWindow(display, screen);
97
98 XTextProperty prop;
99 Atom start = XInternAtom(display,"_IDESK_START", false);
100 cout << " Idesk starting in " << name << endl;
101 prop.value = (unsigned char *)name;
102 prop.encoding = XA_STRING;
103 prop.format = 8;
104 prop.nitems = strlen(name);
105 XSetTextProperty(display, rootWindow, &prop, start);
106
107 XSelectInput( display, rootWindow, PropertyChangeMask| SubstructureNotifyMask);
108 XSync(display, false);
109
110 }
111
initImlib()112 void XDesktopContainer::initImlib()
113 {
114 //Imlib2 stuff
115 imlib_context_set_display(display);
116 imlib_context_set_visual(DefaultVisual(display,DefaultScreen(display)) );
117 imlib_context_set_colormap(DefaultColormap(display,
118 DefaultScreen(display)) );
119 }
120
getRootImage()121 void XDesktopContainer::getRootImage()
122 {
123 DesktopConfig * dConfig = dynamic_cast<DesktopConfig *>(config);
124
125
126 imlib_context_set_drawable(rootWindow);
127
128 bg = new XImlib2Background(this,config);
129
130 bg->InitSpareRoot(rootWindow);
131
132 if(!bg->IsOneShot()){
133 timer = new Timer(this);
134 bg->Finish();
135 }
136 }
137
configure()138 void XDesktopContainer::configure()
139 {
140 //get the user's config file
141 string ideskrcFile = getenv("HOME");
142 ideskrcFile += "/.ideskrc";
143
144 Database db(ideskrcFile);
145 config = new DesktopConfig(db, ideskrcFile);
146
147 DesktopConfig * dConfig = dynamic_cast<DesktopConfig *>(config);
148
149 if(config->numIcons() == 0){
150 dConfig->loadDefaultIcons();
151 }
152
153 locked = dConfig->getLocked();
154 clickDelay = dConfig->getClickSpeed();
155
156 snapState = dConfig->getSnapState();
157 snapShadow = dConfig->getSnapShadow();
158 snapWidth = dConfig->getSnapWidth();
159 snapHeight = dConfig->getSnapHeight();
160
161 actionConfig = new ActionConfig(db, ideskrcFile);
162 }
163
loadIcons()164 void XDesktopContainer::loadIcons()
165 {
166 AbstractIconConfig * iconPtr;
167
168 if (config->numIcons() == 0)
169 {
170 cout << "No icons loaded!! .idesktop is empty or contains invalid icons\n";
171 _exit(1);
172 }
173 else
174 {
175 //iterate through all the icons created by the configure class
176 for(iconPtr = config->start(); config->notFinished();
177 iconPtr = config->nextIcon())
178 {
179 DesktopIconConfig * dIconConfig =
180 dynamic_cast<DesktopIconConfig *>(iconPtr);
181
182 XIcon * icon;
183 if (dIconConfig->getSnapShadow() && dIconConfig->getSnapShadow()){
184 icon = new XIconWithShadow(this, config, iconPtr);
185 }
186 else{
187 icon = new XIcon(this, config, iconPtr);
188
189 }
190 if (icon->isValid()){
191 if(icon->createIcon()){
192 addIcon(icon);
193 }
194 }
195 }
196 }
197 }
198
arrangeIcons()199 void XDesktopContainer::arrangeIcons()
200 {
201 int maxW = 0;
202 int iconX, iconY = 20;
203
204 if( iconList.size() == 0 )
205 {
206 cout << "No Icons! Quitting.\n";
207 _exit(1);
208 }
209
210 for(unsigned int i = 0; i < iconList.size(); i++ )
211 {
212 XIcon *iPtr = dynamic_cast<XIcon *>(iconList[i]);
213 if( iPtr->getWidth() > maxW )
214 maxW = iPtr->getWidth();
215 }
216
217 iconX = widthOfScreen() - maxW - 20;
218
219 for(unsigned int i = 0; i < iconList.size(); i++ )
220 {
221 XIcon *iPtr = dynamic_cast<XIcon *>(iconList[i]);
222
223 if( iconY + iPtr->getHeight() + 30 + iPtr->getFontHeight() >
224 heightOfScreen() )
225 {
226 iconY = 20;
227 iconX = iconX - 20 - maxW;
228 }
229
230 if( iPtr->getX() == 0 && iPtr->getY() == 0 )
231 {
232 iPtr->setX(iconX + ((maxW - iPtr->getWidth())/2));
233 iPtr->setY(iconY);
234 iconY += iPtr->getHeight() + 30 + iPtr->getFontHeight();
235 }
236
237 iPtr->moveImageWindow();
238 iPtr->mapImageWindow();
239 //don't initially map caption for the hover effect
240 iPtr->initMapCaptionWindow();
241
242 //iPtr->draw();
243
244 }
245 }
246
addIcon(AbstractIcon * icon)247 void XDesktopContainer::addIcon(AbstractIcon * icon)
248 {
249 iconList.push_back(icon);
250 }
251
findIcon(Window window)252 XIcon * XDesktopContainer::findIcon(Window window)
253 {
254 for(unsigned int i = 0; i < iconList.size(); i++)
255 {
256 XIcon * tmpIcon = dynamic_cast<XIcon *> (iconList[i]);
257 Window * tmpCapWindow = tmpIcon->getCaptionWindow();
258 if ( *tmpIcon->getImageWindow() == window ||
259 (tmpCapWindow != NULL && *tmpCapWindow == window) )
260 return tmpIcon;
261 }
262 return None;
263 }
264
updateIcons()265 void XDesktopContainer::updateIcons()
266 {
267 for(unsigned int i = 0; i < iconList.size(); i++)
268 {
269 XIcon * tmpIcon = dynamic_cast<XIcon *> (iconList[i]);
270 tmpIcon->draw();
271 }
272 }
273
eventLoop()274 void XDesktopContainer::eventLoop()
275 {
276 XEvent ev;
277 #ifdef HAVE_STARTUP_NOTIFICATION
278 sn_context = NULL;
279 sn_display = NULL;
280 sn_bool_t retval;
281 sn_display = sn_display_new (display,
282 error_trap_push,
283 error_trap_pop);
284
285 #endif /* HAVE_STARTUP_NOTIFICATION */
286
287 for(;;)
288 {
289 if( !XPending( display ) && timer){
290 if(!bg->IsOneShot()){
291 timer->Update();
292 }
293 }
294 else {
295
296 XNextEvent(display, &ev);
297 #ifdef HAVE_STARTUP_NOTIFICATION
298 if (sn_display != NULL){
299 sn_display_process_event (sn_display, &ev);
300 }
301 #endif /* HAVE_STARTUP_NOTIFICATION */
302 event = ev;
303 parseEvent();
304 }
305 }
306
307 #ifdef HAVE_STARTUP_NOTIFICATION
308 sn_launcher_context_unref (sn_context);
309 if (sn_display)
310 {
311 sn_display_unref (sn_display);
312 }
313 #endif /* HAVE_STARTUP_NOTIFICATION */
314 }
315
parseEvent()316 void XDesktopContainer::parseEvent()
317 {
318 currentAction.clear();
319
320 parseNonIconEvents();
321 XIcon * icon = parseIconEvents();
322
323 exeCurrentAction(icon);
324 }
325
parseNonIconEvents()326 void XDesktopContainer::parseNonIconEvents()
327 {
328
329 switch (event.type)
330 {
331
332 case PropertyNotify:
333 //char *name = XGetAtomName(display, event.xproperty.atom );
334 //cout << " Name " << name << endl ;
335
336 static Atom atom_stop = None ;
337 if( atom_stop == None ) atom_stop = XInternAtom(display, "_IDESK_STOP", True);
338
339 if (event.xproperty.atom == atom_stop && !stop){
340 XTextProperty prop;
341 int result = XGetTextProperty(display, rootWindow, &prop, atom_stop);
342 if(result && prop.encoding != None && prop.value != NULL){
343 string current_display_name = DisplayString(display);
344 string old_display_name = (char *)prop.value;
345 string mesg = (char *)prop.value;
346 if(current_display_name == old_display_name){
347 XDeleteProperty (display, rootWindow, atom_stop);
348 cout << "Error ... Idesk is running in " << prop.value << endl;
349 cout << "Exit." << endl;
350 _exit(1);
351 }
352 }
353 }
354
355 static Atom atom_start = None ;
356 if( atom_start == None ) atom_start = XInternAtom(display, "_IDESK_START", True);
357
358 if (event.xproperty.atom == atom_start){
359 XTextProperty prop;
360 int result = XGetTextProperty(display, rootWindow, &prop, atom_start);
361 if(result && prop.encoding != None && prop.value != NULL){
362 string current_display_name = DisplayString(display);
363 string old_display_name = (char *)prop.value;
364 if(current_display_name == old_display_name){
365 XDeleteProperty (display, rootWindow, atom_start);
366 stop = XInternAtom(display,"_IDESK_STOP", false);
367 prop.value = (unsigned char *)current_display_name.c_str();
368 prop.encoding = XA_STRING;
369 prop.format = 8;
370 prop.nitems = strlen(current_display_name.c_str());
371 XSetTextProperty(display, rootWindow, &prop, stop);
372 }
373 }
374 }
375
376 static Atom atom_xroot = None ;
377 if( atom_xroot == None ) atom_xroot = XInternAtom(display, "_XROOTPMAP_ID", True);
378
379 if (event.xproperty.atom == atom_xroot)
380 {
381 Pixmap pmap = bg->GetRootPixmap(event.xproperty.atom);
382 if(bg->pixmap == None){
383 bg->Refresh(pmap);
384 bg->pixmap = (Pixmap)1; //For Fix
385 }else{
386 bg->InitSpareRoot(event.xproperty.window);
387 }
388 updateIcons();
389 }
390 break;
391 }
392 }
393
parseIconEvents()394 XIcon * XDesktopContainer::parseIconEvents()
395 {
396 XIcon * icon;
397
398 icon = findIcon(event.xmotion.window);
399
400 if (icon)
401 {
402 switch (event.type)
403 {
404 case ButtonPress:
405 setEventState();
406
407 if (event.xbutton.button == Button1)
408 currentAction.setLeft(hold);
409 else if (event.xbutton.button == Button2)
410 currentAction.setMiddle(hold);
411 else if (event.xbutton.button == Button3)
412 currentAction.setRight(hold);
413
414 if(event.xbutton.window == *icon->getImageWindow() || event.xbutton.window == *icon->getCaptionWindow()){
415 if(bg->spareRoot){
416 icon->pressImage();
417 }
418 }
419
420 break;
421
422 case MotionNotify:
423 if (icon->isDragging() && !isLocked())
424 icon->dragMotionNotify(event);
425 break;
426
427 case ButtonRelease:
428 setEventState();
429
430 if (event.xbutton.button == Button1)
431 translateButtonRelease(0);
432 else if (event.xbutton.button == Button2)
433 translateButtonRelease(1);
434 else if (event.xbutton.button == Button3)
435 translateButtonRelease(2);
436
437 if(event.xbutton.window == *icon->getImageWindow() || event.xbutton.window == *icon->getCaptionWindow()){
438 if(bg->spareRoot){
439 icon->unpressImage();
440 }
441 }
442
443 break;
444
445 case Expose:
446 //since we are redrawing the whole window we can ignore
447 //multiple expose events and only draw text once
448 if (event.xexpose.count == 0){
449 if(bg->spareRoot){
450 icon->draw();
451 }
452 }
453 break;
454 case EnterNotify:
455 if(event.xcrossing.window == *icon->getImageWindow() || event.xcrossing.window == *icon->getCaptionWindow()){
456 if(bg->spareRoot){
457 icon->mouseOverEffect();
458 icon->event_enter_notify();
459 }
460 }
461 break;
462 case LeaveNotify:
463 if(event.xcrossing.window == *icon->getImageWindow() || event.xcrossing.window == *icon->getCaptionWindow()){
464 if(bg->spareRoot){
465 icon->mouseOffEffect();
466 icon->event_leave_notify();
467 }
468 }
469 break;
470 }
471 }
472 return icon;
473 }
474
exeCurrentAction(XIcon * icon)475 void XDesktopContainer::exeCurrentAction(XIcon * icon)
476 {
477
478 if (actionConfig->getReload()->isOccuring(currentAction)){
479 app->restartIdesk();
480 }
481
482 if (actionConfig->getLock()->isOccuring(currentAction))
483 {
484 toggleLock();
485 DesktopConfig * dConfig = dynamic_cast<DesktopConfig *>(config);
486 dConfig->saveLockState(locked);
487 }
488
489 if (icon) //make sure icon is not NULL
490 {
491 if (actionConfig->getDrag()->isOccuring(currentAction)
492 && !isLocked()
493 && !icon->isDragging() ) //only start drag if not already occuring
494 icon->dragButtonPress(event);
495 else if (actionConfig->getEndDrag()->isOccuring(currentAction))
496 icon->dragButtonRelease(event);
497
498 for (int i = 0; i < icon->getCommandArray().size() &&
499 i < actionConfig->getExecuteActions().size();
500 i++)
501 if (actionConfig->getExecuteAction(i)->isOccuring(currentAction)){
502 #ifdef HAVE_STARTUP_NOTIFICATION
503 if (sn_display != NULL)
504 {
505 sn_context = sn_launcher_context_new (sn_display, DefaultScreen (display));
506 if ((sn_context != NULL) && !sn_launcher_context_get_initiated (sn_context))
507 {
508 sn_launcher_context_set_name (sn_context, icon->getCommand(i).c_str());
509 sn_launcher_context_set_description (sn_context, icon->getCommand(i).c_str());
510 sn_launcher_context_set_binary_name (sn_context, icon->getCommand(i).c_str());
511 sn_launcher_context_set_icon_name(sn_context, icon->getCommand(i).c_str());
512
513 sn_launcher_context_initiate (sn_context,
514 icon->getCommand(i).c_str(),
515 icon->getCommand(i).c_str(),
516 event.xproperty.time);
517 }
518 }
519 #endif /*HAVE_STARTUP_NOTIFICATION */
520 runCommand(icon->getCommand(i));
521 }
522 }
523
524 }
525
setEventState()526 void XDesktopContainer::setEventState()
527 {
528 currentAction.setControl(false);
529 currentAction.setShift(false);
530 currentAction.setAlt(false);
531
532 if (event.xbutton.state & ControlMask) currentAction.setControl(true);
533 if (event.xbutton.state & ShiftMask) currentAction.setShift(true);
534 if (event.xbutton.state & Mod1Mask) currentAction.setAlt(true);
535
536 if (event.xbutton.state & Button1Mask && currentAction.getLeft() == none)
537 currentAction.setLeft(hold);
538
539 if (event.xbutton.state & Button2Mask && currentAction.getMiddle() == none)
540 currentAction.setMiddle(hold);
541
542 if (event.xbutton.state & Button3Mask && currentAction.getRight() == none)
543 currentAction.setRight(hold);
544 }
545
translateButtonRelease(int button)546 void XDesktopContainer::translateButtonRelease(int button)
547 {
548 if (event.xbutton.time - times[button] <= clickDelay)
549 numClicks[button]++;
550 else {
551 numClicks[button] = 1;
552 times[button] = event.xbutton.time;
553 }
554
555 if (numClicks[button] == 1)
556 currentAction.setButton(button, singleClk);
557 else if (numClicks[button] == 2)
558 currentAction.setButton(button, doubleClk);
559 else if (numClicks[button] >= 3)
560 currentAction.setButton(button, tripleClk);
561 else
562 currentAction.setButton(button, none);
563 }
564
saveState()565 void XDesktopContainer::saveState()
566 {
567 //save each of the icons
568 for(unsigned int i = 0; i < iconList.size(); i++)
569 saveIcon(iconList[i]);
570
571 //general config saves
572
573 DesktopConfig * dConfig = dynamic_cast<DesktopConfig *>(config);
574
575 dConfig->saveLockState(locked);
576 }
577
saveIcon(AbstractIcon * xIcon)578 void XDesktopContainer::saveIcon(AbstractIcon * xIcon)
579 {
580 xIcon->save();
581 }
582
reloadState()583 void XDesktopContainer::reloadState()
584 {
585 //TODO -- Reload all of the icons internally instead of rebooting whole
586 // program. Not way too important though.
587 }
588
runCommand(const string & command)589 void XDesktopContainer::runCommand(const string & command)
590 {
591 pid_t pid;
592 //fork and execute program
593 if (pid=fork() != 0) { //Primer proceso hijo
594 #ifdef HAVE_STARTUP_NOTIFICATION
595 if (sn_context != NULL)
596 sn_launcher_context_setup_child_process (sn_context);
597 #endif /* HAVE_STARTUP_NOTIFICATION */
598 setsid();
599 if(execl("/bin/sh", "/bin/sh", "-c", command.c_str(), 0) == -1){
600 printf("Error to execute command %s\n", command.c_str());
601 exit(1);
602 }
603 exit(0); //exit fork
604 }
605 waitpid(pid, NULL, 0);
606 }
widthOfScreen()607 int XDesktopContainer::widthOfScreen()
608 {
609 return WidthOfScreen(DefaultScreenOfDisplay(display));
610 }
611
heightOfScreen()612 int XDesktopContainer::heightOfScreen()
613 {
614 return HeightOfScreen(DefaultScreenOfDisplay(display));
615 }
616
617