1 /* dockapp.c - support functions for a WindowMaker docked application
2  *
3  * From WMAppl (http://www.pobox.com/~charkins/wmappl.html)
4  *
5  * Copyright (C) 2002 Casey Harkins (charkins@pobox.com)
6  *
7  * Authors:
8  * 	Casey Harkins (charkins@pobox.com)
9  * 	Jarek (talen@charybda.icm.edu.pl)
10  *
11  * This is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public
13  * License as published by the Free Software Foundation; either
14  * version 2 of the License, or (at your option) any later version.
15  *
16  * This software is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public
22  * License along with this software; if not, write to the Free
23  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24  */
25 
26 #include "dockapp.h"
27 #ifdef USE_TOOLTIPS
28 #  ifdef HAVE_GETTIMEOFDAY
29 #    include <sys/time.h>
30 #  else
31 #    include <time.h>
32 #  endif
33 #endif
34 #include "options.h"
35 #include "pixmap.h"
36 
37 /* some globals */
38 Display *display;
39 Window draw_window = 0;
40 Window normal_window = 0;
41 Window root_window = 0;
42 GC gc;
43 int paint_requested;
44 
45 extern wmappl_opt *options;
46 
47 /* callback function pointers */
48 void (*paint_ptr) (Display * dsp, Drawable drw, GC g);
49 void (*mouse_ptr) (int x, int y, int b, int s);
50 
51 
52 /* tooltip specific info */
53 #ifdef USE_TOOLTIPS
54 #  ifdef HAVE_GETTIMEOFDAY
55 
56 typedef struct timeval   wmappl_time_t;
57 #    define DEFAULT_TOOLTIP_DELAY  200  // 200 ms
58 #    define wmappl_currenttime(x)  gettimeofday((x),NULL)
59 #    define wmappl_timerclear(x)   timerclear(x)
60 #    define wmappl_timerisset(x)   timerisset(x)
61 #    define wmappl_timerdiff(x,y)  ((((y)->tv_sec - (x)->tv_sec) * 1000000 + ((y)->tv_usec - (x)->tv_usec)) / 1000)
62 
63 #  else
64 
65 typedef time_t           wmappl_time_t;
66 #    define DEFAULT_TOOLTIP_DELAY  1
67 #    define wmappl_currenttime(x)  (time(x))
68 #    define wmappl_timerclear(x)   ((*x)=(time_t)0)
69 #    define wmappl_timerisset(x)   ((*x)!=(time_t)0)
70 #    define wmappl_timerdiff(x,y)  ((*y)-(*x))
71 #  endif
72 
73 	#define TOOLTIP_MAX_WIDTH 45
74 	#define TOOLTIP_GUTTER 5
75 	#define CURSOR_WIDTH 8
76 	#define CURSOR_HEIGHT 16
77 
78 	char * (*tooltip_ptr) (int x, int y);
79 
80 	Tooltip tooltip;
81 	int tooltip_x = 0;
82 	int tooltip_y = 0;
83 	unsigned long tooltip_fg;
84 	unsigned long tooltip_bg;
85 
86 	int           tooltipDelay = DEFAULT_TOOLTIP_DELAY;
87 	wmappl_time_t still_time;
88 #endif
89 
90 
91 /* dockapp_create
92  *	PARAMETERS:
93  *		char * appname    : name of application to be registered with X
94  *		char * geometry   : X geometry string to use when creating the window
95  *		char **interface  : the XPM to use for the background and shape mask
96  *      int withdrawn     : if withdrawn is > 0 then the app is drawn in
97  *                          withdrawn state (WindowMaker DockApps)
98  *                          if withdrawn is zero then it is drawn as a
99  *                          normal window
100  *		int argc          : number of command line parameters
101  *		char **argv		  : array of command line parameters
102  *	RETURN:
103  *		int : returns non-zero on success
104  */
dockapp_create(char * appname,char * geometry,char ** interface,int withdrawn,int argc,char ** argv)105 int dockapp_create(char *appname, char *geometry, char **interface,
106                    int withdrawn, int argc, char **argv) {
107 	/* some X structs */
108 	XSizeHints      xsh;
109 	XWMHints       *xwmh;
110 	XClassHint      xch;
111 	XGCValues       xgcv;
112 	XTextProperty   xtp;
113 	Window          win = 0, iconwin = 0;
114 	Pixmap          pixmap, pixmask;
115 	XpmAttributes   pixattr;
116 	int             screen, win_x, win_y, win_w, win_h, garbage;
117 	unsigned long   blackpixel, whitepixel;
118 
119 	paint_requested = 0;
120 
121 	/* set callbacks to NULL */
122 	mouse_ptr = NULL;
123 	paint_ptr = NULL;
124 
125 #ifdef USE_TOOLTIPS
126 	tooltip_ptr = NULL;
127 	tooltip.win = 0;
128 	if (options->tooltipDelay > 0) {
129 #  ifdef HAVE_GETTIMEOFDAY
130 	  tooltipDelay = options->tooltipDelay;
131 	  if ( tooltipDelay < 10 ) {
132 	    tooltipDelay = 10;
133 	  }
134 #  else
135 	  tooltipDelay = options->tooltipDelay / 1000;
136 	  if ( options->tooltipDelay % 1000 >= 500 ) {
137 	    tooltipDelay++;
138 	  }
139 	  if ( tooltipDelay == 0 ) {
140 	    tooltipDelay++;
141 	  }
142 #  endif
143 	}
144 #endif
145 
146 
147 	/* get screen and root window */
148 	screen = DefaultScreen(display);
149 	root_window = RootWindow(display, screen);
150 
151 	/* get black and white pixel values */
152 	blackpixel = BlackPixel(display, screen);
153 	whitepixel = WhitePixel(display, screen);
154 
155 	/* set Window Manager size hints */
156 	xsh.flags = USSize;
157 	xsh.width = 64;
158 	xsh.height = 64;
159 
160 	XWMGeometry(display, screen, geometry, "64x64+0+0", 0, &xsh, &win_x, &win_y, &win_w, &win_h, &garbage);
161 
162 	/* create the normal window */
163 	win = XCreateSimpleWindow(display, root_window, win_x, win_y, win_w, win_h, 0, blackpixel, whitepixel);
164 	if(!win) {
165 		fprintf(stderr, "dockapp:dockapp_create() - Couldn't create normal window.\n");
166 		return (0);
167 	}
168 
169 	/* set the draw window to the normal window */
170 	draw_window = win;
171 
172 	/* store the normal window */
173 	normal_window = win;
174 
175 	/* if we're using withdrawn state, create the icon window */
176 	if(withdrawn) {
177 		/* create the icon window */
178 		iconwin = XCreateSimpleWindow(display, root_window, win_x, win_y, win_w, win_h, 0, blackpixel, whitepixel);
179 		if(!iconwin) {
180 			fprintf(stderr, "dockapp:dockapp_create() - Couldn't create icon window.\n");
181 			return (0);
182 		}
183 
184 		/* set the draw window to the icon window */
185 		draw_window = iconwin;
186 	}
187 
188 	/* set the valuemask on the XpmAttributes structure */
189 	pixattr.valuemask = 0;
190 
191 	/* load interface pixmap */
192 	if(XpmCreatePixmapFromData(display, root_window, interface, &pixmap, &pixmask, &pixattr) != XpmSuccess) {
193 		fprintf(stderr, "dockapp:dockapp_create() - Couldn't create interface pixmap.\n");
194 		return (0);
195 	}
196 
197 	/* set background pixmap for the draw window */
198 	dockapp_set_background_pixmap(pixmap);
199 
200 	/* shape the draw window if supported */
201 	if(XShapeQueryExtension(display, &garbage, &garbage)) {
202 		XShapeCombineMask(display, draw_window, ShapeBounding, 0, 0, pixmask, ShapeSet);
203 	}
204 	else {
205 		fprintf(stderr, "dockapp:dockapp_create() - Shaped window extension not supported.\n");
206 	}
207 
208 	/* setup Window Manager hints */
209 	xwmh = XAllocWMHints();
210 	xwmh->flags = InputHint | WindowGroupHint;
211 	xwmh->input = True;
212 	xwmh->window_group = win;
213 
214 	/* add withdrawn state and icon window hints if we're using withdrawn */
215 	if(withdrawn) {
216 		xwmh->flags = xwmh->flags | IconWindowHint | StateHint;
217 		xwmh->icon_window = iconwin;
218 		xwmh->initial_state = WithdrawnState;
219 	}
220 	XSetWMHints(display, win, xwmh);
221 
222 	/* set class hints */
223 	xch.res_name = appname;
224 	xch.res_class = appname;
225 	XSetClassHint(display, win, &xch);
226 
227 	/* set size hints */
228 	XSetWMNormalHints(display, win, &xsh);
229 
230 	/* tell window manager app name */
231 	if(!XStringListToTextProperty(&appname, 1, &xtp)) {
232 		fprintf(stderr, "dockapp:dockapp_create() - Couldn't create text property.\n");
233 		return (0);
234 	}
235 	XSetWMName(display, win, &xtp);
236 
237 	/* create a graphics context */
238 	gc = XCreateGC(display, draw_window, GCForeground | GCBackground, &xgcv);
239 	if(!gc) {
240 		fprintf(stderr, "dockapp:dockapp_create() - Couldn't create graphics context.\n");
241 		return (0);
242 	}
243 
244 	/* select events to catch */
245 	XSelectInput(display, draw_window, ExposureMask | ButtonPressMask | ButtonReleaseMask |
246 #ifdef USE_TOOLTIPS
247 				 PointerMotionMask | EnterWindowMask | LeaveWindowMask |
248 #endif
249 				 StructureNotifyMask);
250 
251 	/* set default tooltip foreground and background colors */
252 #ifdef USE_TOOLTIPS
253 	tooltip_fg = dockapp_black_pixel();
254 	tooltip_bg = dockapp_white_pixel();
255 #endif
256 
257 	/* set the command line for restarting */
258 	XSetCommand(display, win, argv, argc);
259 
260 	/* map the main window */
261 	XMapWindow(display, win);
262 
263 	return 1;
264 }								/* end dockapp_create */
265 
266 /* dockapp_run
267  *	PARAMETERS:
268  *		none
269  *	RETURN:
270  *		int : returns non-zero on success
271  */
dockapp_run()272 int dockapp_run() {
273 	int close = 0;
274 	XEvent e;
275 
276 #ifdef USE_TOOLTIPS
277 	wmappl_time_t	now;
278 #endif
279 
280 	while(!close) {
281 
282 		/* sleep while there are no events pending */
283 		while(XPending(display) == 0) {
284 
285 #ifdef USE_TOOLTIPS
286 			if(tooltip_ptr!=NULL) {
287 			        wmappl_currenttime(&now);
288 
289 #if 0
290 				printf ( "Delay = %d, Still = %d, Now = %d\n", tooltipDelay, still_time, now );
291 
292 				printf ( "TimerIsSet = %d, TimerDiff = %d\n",
293 					 wmappl_timerisset(&still_time),
294 					 wmappl_timerdiff(&still_time,&now) );
295 #endif
296 
297 				if(wmappl_timerisset(&still_time) && wmappl_timerdiff(&still_time,&now) > tooltipDelay ) {
298 				  //				        printf ( "*Debug* OK to show tooltip\n" );
299 					wmappl_timerclear(&still_time);
300 					dockapp_show_tooltip(tooltip_x, tooltip_y, tooltip_ptr(tooltip_x, tooltip_y));
301 				}
302 			}
303 #endif
304 
305 			usleep(_DELAY_TIME);
306 		}
307 
308 		/* process all pending events */
309 		while(XPending(display)) {
310 
311 			/* get next event ifrom queue */
312 			XNextEvent(display, &e);
313 
314 			/* handle events */
315 			switch (e.type) {
316 			case ClientMessage:
317 				paint_requested = 1;
318 				break;
319 			case ButtonPress:
320 				if(mouse_ptr)
321 					mouse_ptr(e.xbutton.x, e.xbutton.y, e.xbutton.button, MOUSE_PRESSED);
322 
323 #ifdef USE_TOOLTIPS
324 				/* if visible, hide tooltip */
325 				if(tooltip.win) {
326 					dockapp_hide_tooltip();
327 				}
328 #endif
329 				break;
330 			case ButtonRelease:
331 				if(mouse_ptr)
332 					mouse_ptr(e.xbutton.x, e.xbutton.y, e.xbutton.button, MOUSE_RELEASED);
333 
334 #ifdef USE_TOOLTIPS
335 				/* if visible, hide tooltip */
336 				if(tooltip.win) {
337 					dockapp_hide_tooltip();
338 				}
339 #endif
340 				break;
341 #ifdef USE_TOOLTIPS
342 			case EnterNotify:
343 				break;
344 			case MotionNotify:
345 				if(tooltip_ptr==NULL) {
346 					break;
347 				}
348 
349 				wmappl_timerclear(&still_time);
350 
351 				if(wmappl_currenttime(&still_time)<0) {
352 					fprintf(stderr, "dockapp:dockapp_run() - Could not get time since epoch.\n");
353 					break;
354 				}
355 
356 				tooltip_x=e.xbutton.x;
357 				tooltip_y=e.xbutton.y;
358 
359 				/* if visible, hide tooltip */
360 				if(tooltip.win) {
361 					dockapp_hide_tooltip();
362 				}
363 
364 				break;
365 
366 			case LeaveNotify:
367 				if(tooltip_ptr==NULL) {
368 					break;
369 				}
370 
371 				wmappl_timerclear(&still_time);
372 
373 				/* if visible, hide tooltip */
374 				if(tooltip.win) {
375 					dockapp_hide_tooltip();
376 				}
377 
378 				break;
379 #endif
380 			case Expose:
381 				if(e.xexpose.count != 0)
382 					break;
383 				paint_requested = 1;
384 				break;
385 			case ConfigureNotify:
386 				paint_requested = 1;
387 				break;
388 			case DestroyNotify:
389 				close = 1;
390 				break;
391 			}					/* switch */
392 			/* call paint function if it has been requested */
393 			if(paint_requested) {
394 				paint_requested = 0;
395 				if(paint_ptr)
396 					paint_ptr(display, draw_window, gc);
397 #ifdef USE_TOOLTIPS
398 				if(tooltip.win)
399 					dockapp_update_tooltip();
400 #endif
401 			}
402 		}						/* while XPending */
403 	}							/* while not close */
404 
405 	return 1;
406 } /* end dockapp_run */
407 
408 /* dockapp_redraw - requests that the paint function be called
409  *  PARAMETERS:
410  *      none
411  *  RETURN:
412  *      none
413  */
dockapp_redraw()414 void dockapp_redraw() {
415 	paint_requested = 1;
416 }
417 
418 /* dockapp_clear - clears the draw window
419  *  PARAMETERS:
420  *      none
421  *  RETURN:
422  *      none
423  */
dockapp_clear()424 void dockapp_clear() {
425 	XClearWindow(display, draw_window);
426 }
427 
428 /* dockapp_set_paint
429  *  PARAMETERS:
430  *      void (*func)(Display dsp, Drawable drw, GC g) : pointer to paint
431  *                                                      function
432  *  RETURN:
433  *      int : returns non-zero on success
434  */
dockapp_set_paint(void (* func)(Display * dsp,Drawable drw,GC g))435 int dockapp_set_paint(void (*func) (Display * dsp, Drawable drw, GC g)) {
436 	if(func) {
437 		paint_ptr = func;
438 		return 1;
439 	}
440 	return 0;
441 }
442 
443 /* dockapp_set_mouse
444  *	PARAMETERS:
445  *		void (*func)(int x, int y, int b, int s) : pointer to mouse function
446  *	RETURN:
447  *		int : returns non-zero on success
448  */
dockapp_set_mouse(void (* func)(int x,int y,int b,int s))449 int dockapp_set_mouse(void (*func) (int x, int y, int b, int s)) {
450 	if(func) {
451 		mouse_ptr = func;
452 		return 1;
453 	}
454 	return 0;
455 }
456 
457 /* dockapp_init_display
458  *
459  * RETURN: status. <0 on error, 0 when OK
460  */
dockapp_init_display(void)461 int  dockapp_init_display(void) {
462   /* connect to default display */
463   display = XOpenDisplay((char *) getenv("DISPLAY"));
464   if(!display) {
465     fprintf(stderr, "dockapp:dockapp_init_display() - Couldn't connect to display.\n");
466     return -1;
467   }
468   return 0;
469 }
470 
471 /* dockapp_get_display()
472  *  RETURN:
473  *      Display * : returns display
474  */
dockapp_get_display()475 Display *dockapp_get_display() {
476 	return display;
477 }
478 
479 /* dockapp_get_screen()
480  *  RETURN:
481  *      int : returns screen
482  */
dockapp_get_screen()483 int dockapp_get_screen() {
484 	return DefaultScreen(display);
485 }
486 
487 /* dockapp_black_pixel()
488  *  RETURN:
489  *      unsigned long : black pixel value
490  */
dockapp_black_pixel()491 unsigned long dockapp_black_pixel() {
492 	return BlackPixel(display, dockapp_get_screen());
493 
494 }
495 
496 /* dockapp_white_pixel()
497  *  RETURN:
498  *      unsigned long : white pixel value
499  */
dockapp_white_pixel()500 unsigned long dockapp_white_pixel() {
501 	return WhitePixel(display, dockapp_get_screen());
502 
503 }
504 
505 /* dockapp_to_screen_coords
506  *   PARAMETERS:
507  *     int x             : x coordinate relative to origin of draw_window
508  *     int y             : y coordinate relative to origin of draw_window
509  *     int *x_return     : returned x coordinate relative to root window
510  *     int *y_return     : returned y coordinate relative to root window
511  *   RETURN:
512  *     int               : returns non-zero on success
513  */
dockapp_to_screen_coords(int x,int y,int * x_return,int * y_return)514 int dockapp_to_screen_coords(int x, int y, int *x_return, int *y_return) {
515 	Window c;
516 
517 	if(XTranslateCoordinates(display, draw_window, root_window, x, y, x_return, y_return, &c) == 0) {
518 		fprintf(stderr, "dockapp:get_screen_coordinates() - Translate error!\n");
519 	}
520 	return 1;
521 }
522 
523 /* dockapp_set_background_color
524  *  PARAMETERS:
525  *	  char *: background color name
526  *
527  */
dockapp_set_background_color(char * color)528 void dockapp_set_background_color(char *color) {
529 	XColor xcolor;
530 	Colormap colormap = XDefaultColormap(display, dockapp_get_screen());
531 
532 	if(XParseColor(display, colormap, color, &xcolor)==0) {
533 		fprintf(stderr, "Could not parse color: %s\n", color);
534 		return;
535 	}
536 	if(XAllocColor(display, colormap, &xcolor)==0) {
537 		fprintf(stderr, "Could not allocate color: %s\n", color);
538 		return;
539 	}
540 	dockapp_set_background_pixel(xcolor.pixel);
541 }
542 
543 /* dockapp_set_background_pixel
544  *  PARAMETERS:
545  *	  unsigned long pixel : background color
546  *
547  */
dockapp_set_background_pixel(unsigned long pixel)548 void dockapp_set_background_pixel(unsigned long pixel) {
549 	XSetWindowBackground(display, draw_window, pixel);
550 	dockapp_clear();
551 }
552 
553 /* dockapp_set_background_pixmap_data
554  *  PARAMETERS:
555  *	  char **data : background pixmap
556  *
557  */
dockapp_set_background_pixmap_data(char ** data)558 void dockapp_set_background_pixmap_data(char **data) {
559 	XpmAttributes   pixattr;
560 	Pixmap pixmap, pixmask;
561 
562 	/* set the valuemask on the XpmAttributes structure */
563 	pixattr.valuemask = 0;
564 
565 	/* load pixmap */
566 	if(XpmCreatePixmapFromData(display, root_window, data, &pixmap, &pixmask, &pixattr) != XpmSuccess) {
567 		return;
568 	}
569 
570 	dockapp_set_background_pixmap(pixmap);
571 }
572 
573 /* dockapp_set_background_pixmap_file
574  *  PARAMETERS:
575  *    char *data : absolute path to background pixmap
576  *
577  */
dockapp_set_background_pixmap_file(char * file)578 void dockapp_set_background_pixmap_file(char *file) {
579 	XpmAttributes   pixattr;
580 	Pixmap pixmap, pixmask;
581 
582 	/* set the valuemask on the XpmAttributes structure */
583 	pixattr.valuemask = 0;
584 
585 	/* load pixmap */
586 	create_button_pixmap(display, file, &pixmap, &pixmask, &pixattr);
587 
588 	dockapp_set_background_pixmap(pixmap);
589 }
590 
591 /* dockapp_set_background_pixmap
592  *  PARAMETERS:
593  *	  Pixmap pixmap : background pixmap
594  *
595  */
dockapp_set_background_pixmap(Pixmap pixmap)596 void dockapp_set_background_pixmap(Pixmap pixmap) {
597 	XSetWindowBackgroundPixmap(display, draw_window, pixmap);
598 	dockapp_clear();
599 }
600 
601 #ifdef USE_TOOLTIPS
602 
603 /* dockapp_set_tooltip_foreground
604  *  PARAMETERS:
605  *	  char *: tooltip foreground color name
606  *
607  */
dockapp_set_tooltip_foreground(char * color)608 void dockapp_set_tooltip_foreground(char *color) {
609 	XColor xcolor;
610 	Colormap colormap = XDefaultColormap(display, dockapp_get_screen());
611 
612 	if(XParseColor(display, colormap, color, &xcolor)==0) {
613 		fprintf(stderr, "Could not parse color: %s\n", color);
614 		return;
615 	}
616 	if(XAllocColor(display, colormap, &xcolor)==0) {
617 		fprintf(stderr, "Could not allocate color: %s\n", color);
618 		return;
619 	}
620 	tooltip_fg=xcolor.pixel;
621 }
622 
623 /* dockapp_set_tooltip_background
624  *  PARAMETERS:
625  *	  char *: tooltip background color name
626  *
627  */
dockapp_set_tooltip_background(char * color)628 void dockapp_set_tooltip_background(char *color) {
629 	XColor xcolor;
630 	Colormap colormap = XDefaultColormap(display, dockapp_get_screen());
631 
632 	if(XParseColor(display, colormap, color, &xcolor)==0) {
633 		fprintf(stderr, "Could not parse color: %s\n", color);
634 		return;
635 	}
636 	if(XAllocColor(display, colormap, &xcolor)==0) {
637 		fprintf(stderr, "Could not allocate color: %s\n", color);
638 		return;
639 	}
640 	tooltip_bg=xcolor.pixel;
641 }
642 
643 /* dockapp_set_tooltip
644  *	PARAMETERS:
645  *		char * (*func)(int x, int y) : pointer to tooltip function
646  *
647  *	RETURN:
648  *		int : returns non-zero on success
649  */
dockapp_set_tooltip(char * (* func)(int x,int y))650 int dockapp_set_tooltip(char * (*func) (int x, int y)) {
651 	if(func) {
652 		tooltip_ptr = func;
653 		return 1;
654 	}
655 	return 0;
656 }								/* end tooltip_func */
657 
658 /* dockapp_show_tooltip(int x, int y, char *string)
659  *  PARAMETERS:
660  *    int x             : x coordinate of "tip" of the tooltip
661  *    int y             : y coordinate of "tip" of the tooltip
662  *    char *string      : the string to be displayed in the tooltip
663  *
664  *  RETURN:
665  *    int : non-zero on success
666  */
dockapp_show_tooltip(int x,int y,char * string)667 int dockapp_show_tooltip(int x, int y, char *string) {
668 	XGCValues       gcvals;
669 	XSizeHints      sizehints;
670 	XSetWindowAttributes winattribs;
671 
672 	if(tooltip.win) {
673 		dockapp_hide_tooltip();
674 	}
675 
676 
677 	/* return if string is NULL */
678 	if(string==NULL) {
679 		return 0;
680 	}
681 
682 	/* query the font if it hasn't been already */
683 	if(!tooltip.font) {
684 		if(options->tooltipfont) {
685 			tooltip.font = XLoadQueryFont(display, options->tooltipfont);
686 		}
687 		if(tooltip.font == NULL) {
688 			tooltip.font = XLoadQueryFont(display, "fixed");
689 		}
690 		if(tooltip.font == NULL) {
691 			fprintf(stderr, "dockapp:dockapp_show_tooltip() - Couldn't load fixed font!\n");
692 			return (0);
693 		}
694 	}
695 
696 	/* process string into lines of appropriate width */
697 	dockapp_process_tooltip(string);
698 
699 	/* map the tooltip window */
700 	/* set the size hints structure */
701 	sizehints.flags = USSize | USPosition;
702 
703 	dockapp_to_screen_coords(x, y, &sizehints.x, &sizehints.y);
704 
705 	/* sizehints.x = x;
706 	   sizehints.y = y; */
707 
708 	if(tooltip.lines == 1) {
709 		sizehints.width = XTextWidth(tooltip.font, tooltip.text[0], strlen(tooltip.text[0])) + 2;
710 	}
711 	else if(tooltip.lines > 1) {
712 		sizehints.width = XTextWidth(tooltip.font, tooltip.text[0], strlen(tooltip.text[0])) + 4;
713 	}
714 	else {
715 		sizehints.width = 0;
716 	}
717 	sizehints.height = ((tooltip.font->ascent + tooltip.font->descent + 4) * tooltip.lines);
718 
719 	/* ensure tooltip is entirely on screen */
720 	dockapp_tooltip_location(&sizehints);
721 
722 	/* create the window */
723 	tooltip.win = XCreateSimpleWindow(display, DefaultRootWindow(display),
724 									  sizehints.x, sizehints.y, sizehints.width, sizehints.height,
725 									  0, dockapp_black_pixel(), dockapp_white_pixel());
726 	if(!tooltip.win) {
727 		fprintf(stderr, "dockapp:dockapp_show_tooltip() - Couldn't create tooltip window!\n");
728 		return (0);
729 	}
730 
731 	/* set the size hints */
732 	XSetWMNormalHints(display, tooltip.win, &sizehints);
733 
734 	/* set the window attributes */
735 	winattribs.save_under = True;
736 	winattribs.override_redirect = True;
737 	XChangeWindowAttributes(display, tooltip.win, CWSaveUnder | CWOverrideRedirect, &winattribs);
738 
739 	/* set the window name */
740 	XStoreName(display, tooltip.win, "tooltip");
741 
742 	/* select to receive exposure events */
743 	XSelectInput(display, tooltip.win, ExposureMask);
744 
745 	/* create the graphics context if it hasn't been created */
746 	if(!tooltip.gc) {
747 		gcvals.foreground = tooltip_fg;
748 		gcvals.background = tooltip_bg;
749 		gcvals.graphics_exposures = 0;
750 		tooltip.gc = XCreateGC(display, tooltip.win, GCForeground | GCBackground | GCGraphicsExposures, &gcvals);
751 		if(tooltip.gc == NULL) {
752 			fprintf(stderr, "dockapp:dockapp_show_tooltip() - Couldn't create graphics context!\n");
753 			return (0);
754 		}
755 
756 		XSetFont(display, tooltip.gc, tooltip.font->fid);
757 	}
758 
759 	XSetWindowBackground(display, tooltip.win, tooltip_bg);
760 
761 	XMapRaised(display, tooltip.win);
762 
763 	return 1;
764 }
765 
dockapp_hide_tooltip()766 int dockapp_hide_tooltip() {
767 	int i = 0;
768 
769 	if(!tooltip.win)
770 		return -1;
771 	XUnmapWindow(display, tooltip.win);
772 	for(i = 0; i < tooltip.lines; i++) {
773 		free(tooltip.text[i]);
774 	}
775 	free(tooltip.text);
776 	tooltip.lines = 0;
777 	tooltip.win = 0;
778 	XFlush(display);
779 	return 1;
780 }
781 
dockapp_update_tooltip()782 void dockapp_update_tooltip() {
783 	int i=0;
784 
785 	if(!tooltip.win || !tooltip.gc || !tooltip.text) {
786 		return;
787 	}
788 
789 	for(i = 0; i < tooltip.lines; i++) {
790 		XDrawString(display, tooltip.win, tooltip.gc, 1,
791 					(i + 1) * (tooltip.font->ascent + tooltip.font->descent + 4) - 4,
792 					tooltip.text[i], strlen(tooltip.text[i]));
793 	}
794 
795 
796 	XFlush(display);
797 }
798 
799 
dockapp_process_tooltip(char * string)800 void dockapp_process_tooltip(char *string)
801 {
802 	int             length = 0;
803 	int             size = 0;
804 	int             i = 0;
805 	char           *pointer = NULL;
806 
807 	length = strlen(string);
808 
809 	/* calculate number of lines */
810 	tooltip.lines = length / TOOLTIP_MAX_WIDTH;
811 	if(length % TOOLTIP_MAX_WIDTH != 0)
812 		tooltip.lines++;
813 
814 	/* allocate pointers to the lines */
815 	tooltip.text = (char **) malloc(sizeof(char *) * tooltip.lines);
816 
817 	pointer = string;
818 
819 	for(i = 0; i < tooltip.lines; i++) {
820 		/* allocate the line */
821 		if(length < TOOLTIP_MAX_WIDTH) {
822 			tooltip.text[i] = (char *) malloc(sizeof(char) * (length + 1));
823 			size = length;
824 		}
825 		else {
826 			tooltip.text[i] = (char *) malloc(sizeof(char) * (TOOLTIP_MAX_WIDTH + 1));
827 			size = TOOLTIP_MAX_WIDTH;
828 		}
829 
830 		strncpy(tooltip.text[i], pointer, size);
831 		tooltip.text[i][size] = '\0';
832 
833 		length = length - size;
834 		pointer = &pointer[size];
835 
836 	}
837 }
838 
839 
dockapp_tooltip_location(XSizeHints * sizehints)840 void dockapp_tooltip_location(XSizeHints *sizehints) {
841 
842 	XWindowAttributes rootattr;
843 
844 	if(XGetWindowAttributes(display, root_window, &rootattr) == 0) {
845 		fprintf(stderr, "dockapp:set_tooltip_location() - Couldn't determine screen size, tooltips may run off of the screen.\n");
846 		return;
847 	}
848 
849 	/* check if tooltip should go to the right or left of the mouse cursor */
850 	if(sizehints->x < rootattr.width/2) {
851 		/* tooltip goes to the right, need to account for mouse cursor width */
852 		sizehints->x=sizehints->x+TOOLTIP_GUTTER+CURSOR_WIDTH;
853 	}
854 	else {
855 		/* tooltip goes to the left, need to account for the tooltip width */
856 		sizehints->x=sizehints->x-TOOLTIP_GUTTER-sizehints->width;
857 	}
858 
859 	/* check if tooltip should go above or below the mouse cursor */
860 	if(sizehints->y < rootattr.height/2) {
861 		/* tooltip goes below, need to account for mouse cursor height */
862 		sizehints->y=sizehints->y+TOOLTIP_GUTTER+CURSOR_HEIGHT;
863 	}
864 	else {
865 		/* tooltip goes above, need to account for tooltip height */
866 		sizehints->y=sizehints->y-TOOLTIP_GUTTER-sizehints->height;
867 	}
868 
869 }
870 
871 
872 
873 #endif
874