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