1 /* Patched Version - all changes to this code have been done by
2  * Frank Hale with exception of the virtual desktop buttons. Somebody
3  * sent me a patch for that.
4  *
5  * frankhale@yahoo.com
6  * Date - 17 Nov 2001
7  */
8 
9 /********************************************************
10  ** F***ing Small Panel 0.7 Copyright (c) 2000-2001 By **
11  ** Peter Zelezny <zed@linuxpower.org>                 **
12  ** See file COPYING for license details.              **
13  ********************************************************/
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <time.h>
18 #include <sys/time.h>
19 #include <sys/types.h>
20 #include <unistd.h>
21 
22 #include <X11/Xlib.h>
23 #include <X11/Xproto.h>
24 #include <X11/Xutil.h>
25 #include <X11/Xatom.h>
26 
27 #ifdef HAVE_XPM
28 #include <X11/xpm.h>
29 #include "icon.xpm"
30 #endif
31 
32 #include "fspanel.h"
33 
34 /* you can edit these */
35 #define MAX_TASK_WIDTH 145
36 #define PAGER_BOX_WIDTH 24
37 #define ICONWIDTH 16
38 #define ICONHEIGHT 16
39 #define WINHEIGHT 24
40 #define WINWIDTH (scr_width)
41 #define FONT_NAME "-*-lucida*-m*-r-*-*-12-*-*"
42 
43 /* don't edit these */
44 #define TEXTPAD 6
45 #define left_arrow_x 18
46 #define right_arrow_x 30
47 
48 Display *dd;
49 Window root_win;
50 Pixmap generic_icon;
51 Pixmap generic_mask;
52 GC fore_gc;
53 XFontStruct *xfs;
54 int scr_screen;
55 int scr_depth;
56 int scr_width;
57 int scr_height;
58 int text_y;
59 int pager_width;
60 /*int time_width;*/
61 Window win;
62 
63 unsigned short cols[] = {
64 	0xd75c, 0xd75c, 0xd75c,		  /* 0. light gray */
65 	0xbefb, 0xbaea, 0xbefb,		  /* 1. mid gray */
66 	0xaefb, 0xaaea, 0xaefb,		  /* 2. dark gray */
67 	0xefbe, 0xefbe, 0xefbe,		  /* 3. white */
68 	0x8617, 0x8207, 0x8617,		  /* 4. darkest gray */
69 	0x0000, 0x0000, 0x0000		  /* 5. black */
70 };
71 
72 #define PALETTE_COUNT (sizeof (cols) / sizeof (cols[0]) / 3)
73 
74 unsigned long palette[PALETTE_COUNT];
75 
76 char *atom_names[] = {
77 	"KWM_WIN_ICON",
78 	"_MOTIF_WM_HINTS",
79 	"_NET_CURRENT_DESKTOP",
80 	"_WIN_WORKSPACE",
81 	"_NET_WM_STATE_SKIP_TASKBAR",
82 	"_NET_WM_STATE_SKIP_PAGER",
83 	"_WIN_HINTS",
84 	/*"_NET_WM_STRUT",*/
85 	"_WIN_LAYER",
86 	"_NET_CLIENT_LIST",
87 	"_WIN_CLIENT_LIST",
88 	"_NET_NUMBER_OF_DESKTOPS",
89 	"_WIN_WORKSPACE_COUNT",
90 	"_NET_WM_STATE_STICKY",
91 	"_WIN_STATE",
92 	"WM_STATE"
93 };
94 
95 #define ATOM_COUNT (sizeof (atom_names) / sizeof (atom_names[0]))
96 
97 Atom net_wm_strut;
98 
99 Atom atoms[ATOM_COUNT];
100 
101 #define atom_KWM_WIN_ICON atoms[0]
102 #define atom__MOTIF_WM_HINTS atoms[1]
103 #define atom__NET_CURRENT_DESKTOP atoms[2]
104 #define atom__WIN_WORKSPACE atoms[3]
105 #define atom__NET_WM_STATE_SKIP_TASKBAR atoms[4]
106 #define atom__NET_WM_STATE_SKIP_PAGER atoms[5]
107 #define atom__WIN_HINTS atoms[6]
108 /*#define atom__NET_WM_STRUT atoms[ ]*/
109 #define atom__WIN_LAYER atoms[7]
110 #define atom__NET_CLIENT_LIST atoms[8]
111 #define atom__WIN_CLIENT_LIST atoms[9]
112 #define atom__NET_NUMBER_OF_DESKTOPS atoms[10]
113 #define atom__WIN_WORKSPACE_COUNT atoms[11]
114 #define atom__NET_WM_STATE_STICKY atoms[12]
115 #define atom__WIN_STATE atoms[13]
116 #define atom_WM_STATE atoms[14]
117 
118 
119 /*************************************/
120 /* CODE BELOW GETS/SETS X PROPERTIES */
121 /*************************************/
122 
switch_desk(taskbar * tb,int rel)123 void switch_desk (taskbar * tb, int rel)
124 {
125 	XClientMessageEvent xev;
126 	unsigned long *data;
127 	int want = tb->my_desktop + rel, protocol;
128 
129 	if (want < 0)
130 		return;
131 
132 	/* try unified window spec first (protocol 0) */
133 	protocol = 0;
134 	data = get_prop_data (root_win, atom__NET_NUMBER_OF_DESKTOPS, XA_CARDINAL, 0);
135 
136 	/* failed, let's try gnome (protocol 1) */
137 	if (!data)
138 	{
139 		protocol = 1;
140 		data = get_prop_data (root_win, atom__WIN_WORKSPACE_COUNT, XA_CARDINAL, 0);
141 	}
142 
143 	if (data)
144 	{
145 		register unsigned long max_desks = *data;
146 		XFree (data);
147 		if (max_desks <= want)
148 			return;
149 	}
150 
151 	xev.type = ClientMessage;
152 	xev.window = root_win;
153 
154 	/* send it with the right protocol */
155 	if (protocol == 0)
156 		xev.message_type = atom__NET_CURRENT_DESKTOP;
157 	else if (protocol == 1)
158 		xev.message_type = atom__WIN_WORKSPACE;
159 
160 	xev.format = 32;
161 	xev.data.l[0] = want;
162 	XSendEvent (dd, root_win, False, SubstructureNotifyMask, (XEvent *) &xev);
163 }
164 
set_bottom_strut()165 void set_bottom_strut()
166 {
167 	CARD32 strut[] = { 0,0,0,WINHEIGHT+3 };
168 	XChangeProperty (dd, win, net_wm_strut, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &strut, 4);
169 }
170 
set_top_strut()171 void set_top_strut()
172 {
173 	CARD32 strut[] = { 0,0,WINHEIGHT+1,0 };
174 	XChangeProperty (dd, win, net_wm_strut, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &strut, 4);
175 }
176 
turn_off_strut()177 void turn_off_strut()
178 {
179   	XDeleteProperty(dd, win, net_wm_strut);
180 }
181 
get_prop_data(Window win,Atom prop,Atom type,int * items)182 unsigned long*get_prop_data (Window win, Atom prop, Atom type, int *items)
183 {
184 	Atom type_ret;
185 	int format_ret;
186 	unsigned long items_ret;
187 	unsigned long after_ret;
188 	unsigned char *prop_data;
189 
190 	prop_data = 0;
191 
192 	XGetWindowProperty (dd, win, prop, 0, 0x7fffffff, False,
193 							  type, &type_ret, &format_ret, &items_ret,
194 							  &after_ret, &prop_data);
195 	if (items)
196 		*items = items_ret;
197 
198 	return (unsigned long *)prop_data;
199 }
200 
get_task_hinticon(task * tk)201 void get_task_hinticon (task *tk)
202 {
203 	XWMHints *hin;
204 
205 	tk->icon = None;
206 	tk->mask = None;
207 
208 	hin = (XWMHints *) get_prop_data (tk->win, XA_WM_HINTS, XA_WM_HINTS, 0);
209 	if (hin)
210 	{
211 		if ((hin->flags & IconPixmapHint))
212 		{
213 			if ((hin->flags & IconMaskHint))
214 			{
215 				tk->mask = hin->icon_mask;
216 			}
217 
218 			tk->icon = hin->icon_pixmap;
219 			tk->icon_copied = 1;
220 			scale_icon (tk);
221 		}
222 		XFree (hin);
223 	}
224 
225 	if (tk->icon == None)
226 	{
227 		tk->icon = generic_icon;
228 		tk->mask = generic_mask;
229 	}
230 }
231 
get_task_kdeicon(task * tk)232 void get_task_kdeicon (task *tk)
233 {
234 	unsigned long *data;
235 
236 	data = get_prop_data (tk->win, atom_KWM_WIN_ICON, atom_KWM_WIN_ICON, 0);
237 	if (data)
238 	{
239 		tk->icon = data[0];
240 		tk->mask = data[1];
241 		XFree (data);
242 	}
243 }
244 
find_desktop(Window win)245 int find_desktop (Window win)
246 {
247 	int desk = 0;
248 	unsigned long *data;
249 
250 	/* try unified window spec first */
251 	data = get_prop_data (win, atom__NET_CURRENT_DESKTOP, XA_CARDINAL, 0);
252 
253 	if (!data)
254 	/* failed, let's try gnome */
255 		data = get_prop_data (win, atom__WIN_WORKSPACE, XA_CARDINAL, 0);
256 
257 	if (data)
258 	{
259 		desk = *data;
260 		XFree (data);
261 	}
262 	return desk;
263 }
264 
is_hidden(Window win)265 int is_hidden (Window win)
266 {
267 	unsigned long *data;
268 	int ret = 0;
269 
270 	/* try unified window spec first (two hints) */
271 	data = get_prop_data (win, atom__NET_WM_STATE_SKIP_TASKBAR, XA_CARDINAL, 0);
272 
273 	if (!data)
274 		data = get_prop_data (win, atom__NET_WM_STATE_SKIP_PAGER, XA_CARDINAL, 0);
275 
276 	if (data)
277 	/* if we got one, we're done */
278 	{
279 		ret = 1;
280 		XFree (data);
281 	}
282 
283 	else
284 	/* failed, let's try gnome (one hint, three bits) */
285 	{
286 		data = get_prop_data (win, atom__WIN_HINTS, XA_CARDINAL, 0);
287 		if (data)
288 		{
289 			if (((*data) & WIN_HINTS_SKIP_FOCUS) ||	((*data) & WIN_HINTS_SKIP_WINLIST) || ((*data) & WIN_HINTS_SKIP_TASKBAR))
290 			/* if we got one, we're done */
291 				ret = 1;
292 			XFree (data);
293 		}
294 	}
295 	return ret;
296 }
297 
is_iconified(Window win)298 int is_iconified (Window win)
299 {
300 	unsigned long *data;
301 	int ret = 0;
302 
303 	data = get_prop_data (win, atom_WM_STATE, atom_WM_STATE, 0);
304 	if (data)
305 	{
306 		if (data[0] == IconicState)
307 			ret = 1;
308 		XFree (data);
309 	}
310 	return ret;
311 }
312 
set_prop(Window win,Atom at,long val)313 void set_prop (Window win, Atom at, long val)
314 {
315 	XChangeProperty (dd, win, at, XA_CARDINAL, 32,
316 		PropModeReplace, (unsigned char *) &val, 1);
317 }
318 
319 /*************************************/
320 /* CODE ABOVE GETS/SETS X PROPERTIES */
321 /*************************************/
322 
323 
324 
325 
326 
327 
328 
329 /**************************************/
330 /* CODE BELOW DOES GRAPHICS OPERATIONS*/
331 /**************************************/
332 
gui_init(void)333 void gui_init (void)
334 {
335 	XGCValues gcv;
336 	XColor xcl;
337 	int i, j;
338 	char *fontname;
339 
340 	i = j = 0;
341 	do
342 	{
343 		xcl.red = cols[i];
344 		i++;
345 		xcl.green = cols[i];
346 		i++;
347 		xcl.blue = cols[i];
348 		i++;
349 		XAllocColor (dd, DefaultColormap (dd, scr_screen), &xcl);
350 		palette[j] = xcl.pixel;
351 		j++;
352 	}
353 	while (j < PALETTE_COUNT);
354 
355 	fontname = FONT_NAME;
356 	do
357 	{
358 		xfs = XLoadQueryFont (dd, fontname);
359 		fontname = "fixed";
360 	}
361 	while (!xfs);
362 
363 	/*time_width = XTextWidth (xfs, "88:88", 5); */
364 #define time_width (35)
365 	text_y = xfs->ascent + ((WINHEIGHT - xfs->ascent) / 2);
366 
367 	gcv.font = xfs->fid;
368 	gcv.graphics_exposures = False;
369 	fore_gc = XCreateGC (dd, root_win, GCFont | GCGraphicsExposures, &gcv);
370 
371 #ifdef HAVE_XPM
372 	XpmCreatePixmapFromData (dd, root_win, icon_xpm, &generic_icon,
373 									 &generic_mask, NULL);
374 #else
375 	generic_icon = 0;
376 #endif
377 }
378 
set_foreground(int index)379 void set_foreground (int index)
380 {
381 	XSetForeground (dd, fore_gc, palette[index]);
382 }
383 
draw_line(taskbar * tb,int x,int y,int a,int b)384 void draw_line (taskbar *tb, int x, int y, int a, int b)
385 {
386 	XDrawLine (dd, tb->win, fore_gc, x, y, a, b);
387 }
388 
fill_rect(taskbar * tb,int x,int y,int a,int b)389 void fill_rect (taskbar *tb, int x, int y, int a, int b)
390 {
391 	XFillRectangle (dd, tb->win, fore_gc, x, y, a, b);
392 }
393 
scale_icon(task * tk)394 void scale_icon (task *tk)
395 {
396 	int xx, yy, x, y;
397 	unsigned int w, h, d, bw;
398 	Pixmap pix, mk = None;
399 	XGCValues gcv;
400 	GC mgc=None;
401 
402 	XGetGeometry (dd, tk->icon, &pix, &x, &y, &w, &h, &bw, &d);
403 	pix = XCreatePixmap (dd, tk->win, ICONWIDTH, ICONHEIGHT, scr_depth);
404 
405 	if (tk->mask != None)
406 	{
407 		mk = XCreatePixmap (dd, tk->win, ICONWIDTH, ICONHEIGHT, 1);
408 		gcv.subwindow_mode = IncludeInferiors;
409 		gcv.graphics_exposures = False;
410 		mgc = XCreateGC (dd, mk, GCGraphicsExposures | GCSubwindowMode, &gcv);
411 	}
412 
413 	set_foreground (3);
414 
415 	/* this is my simple & dirty scaling routine */
416 	for (y = ICONHEIGHT - 1; y >= 0; y--)
417 	{
418 		yy = (y * h) / ICONHEIGHT;
419 		for (x = ICONWIDTH - 1; x >= 0; x--)
420 		{
421 			xx = (x * w) / ICONWIDTH;
422 			if (d != scr_depth)
423 				XCopyPlane (dd, tk->icon, pix, fore_gc, xx, yy, 1, 1, x, y, 1);
424 			else
425 				XCopyArea (dd, tk->icon, pix, fore_gc, xx, yy, 1, 1, x, y);
426 			if (mk != None)
427 				XCopyArea (dd, tk->mask, mk, mgc, xx, yy, 1, 1, x, y);
428 		}
429 	}
430 
431 	if (mk != None)
432 	{
433 		XFreeGC (dd, mgc);
434 		tk->mask = mk;
435 	}
436 
437 	tk->icon = pix;
438 }
439 
gui_draw_vline(taskbar * tb,int x)440 void gui_draw_vline (taskbar * tb, int x)
441 {
442 	set_foreground (4);
443 	draw_line (tb, x, 0, x, WINHEIGHT);
444 	set_foreground (3);
445 	draw_line (tb, x + 1, 0, x + 1, WINHEIGHT);
446 }
447 
gui_draw_task(taskbar * tb,task * tk)448 void gui_draw_task (taskbar * tb, task * tk)
449 {
450 	int len;
451 	int x = tk->pos_x;
452 	int taskw = tk->width;
453 
454 	if (!tk->name)
455 		return;
456 
457 	gui_draw_vline (tb, x);
458 
459 /*set_foreground (3); *//* it's already 3 from gui_draw_vline() */
460 	draw_line (tb, x + 1, 0, x + taskw, 0);
461 
462 	set_foreground (1);
463 	draw_line (tb, x + 1, WINHEIGHT - 1, x + taskw, WINHEIGHT - 1);
464 
465 	if (tk->focused)
466 	{
467 		x++;
468 		/*set_foreground (1);*/		  /* mid gray */
469 		fill_rect (tb, x + 3, 3, taskw - 5, WINHEIGHT - 6);
470 		set_foreground (3);		  /* white */
471 		draw_line (tb, x + 2, WINHEIGHT - 2, x + taskw - 2, WINHEIGHT - 2);
472 		draw_line (tb, x + taskw - 2, 2, x + taskw - 2, WINHEIGHT - 2);
473 		set_foreground (0);
474 		draw_line (tb, x + 1, 2, x + 1, WINHEIGHT - 2);
475 		set_foreground (4);		  /* darkest gray */
476 		draw_line (tb, x + 2, 2, x + taskw - 2, 2);
477 		draw_line (tb, x + 2, 2, x + 2, WINHEIGHT - 3);
478 	} else
479 	{
480 		set_foreground (0);		  /* mid gray */
481 		fill_rect (tb, x + 2, 1, taskw - 1, WINHEIGHT - 2);
482 	}
483 
484 	{
485 		register int text_x = x + TEXTPAD + TEXTPAD + ICONWIDTH;
486 
487 		/* check how many chars can fit */
488 		len = strlen (tk->name);
489 		while (XTextWidth (xfs, tk->name, len) >= taskw - (text_x - x) - 2
490 				 && len > 0)
491 			len--;
492 
493 		if (tk->iconified)
494 		{
495 			/* draw task's name dark (iconified) */
496 			set_foreground (3);
497 			XDrawString (dd, tb->win, fore_gc, text_x, text_y + 1, tk->name,
498 							 len);
499 			set_foreground (4);
500 		} else
501 		{
502 			set_foreground (5);
503 		}
504 
505 		/* draw task's name here */
506 		XDrawString (dd, tb->win, fore_gc, text_x, text_y, tk->name, len);
507 	}
508 
509 #ifndef HAVE_XPM
510 	if (!tk->icon)
511 		return;
512 #endif
513 
514 	/* draw the task's icon */
515 	XSetClipMask (dd, fore_gc, tk->mask);
516 	XSetClipOrigin (dd, fore_gc, x + TEXTPAD, (WINHEIGHT - ICONHEIGHT) / 2);
517 	XCopyArea (dd, tk->icon, tb->win, fore_gc, 0, 0, ICONWIDTH, ICONHEIGHT,
518 				  x + TEXTPAD, (WINHEIGHT - ICONHEIGHT) / 2);
519 	XSetClipMask (dd, fore_gc, None);
520 }
521 
gui_draw_clock(taskbar * tb)522 void gui_draw_clock (taskbar * tb)
523 {
524 	char *time_str;
525 	time_t now;
526 	int width, old_x, x = WINWIDTH - time_width - (TEXTPAD * 4);
527 
528 	old_x = x;
529 
530 	width = WINWIDTH - x - 2;
531 
532 	now = time (0);
533 	time_str = ctime (&now) + 11;
534 
535 	gui_draw_vline (tb, x);
536 	x += TEXTPAD;
537 
538 /*set_foreground (3); *//* white *//* it's already 3 from gui_draw_vline() */
539 	draw_line (tb, x + 1, WINHEIGHT - 2, old_x + width - TEXTPAD,
540 				  WINHEIGHT - 2);
541 	draw_line (tb, old_x + width - TEXTPAD, 2, old_x + width - TEXTPAD,
542 				  WINHEIGHT - 2);
543 
544 	set_foreground (1);			  /* mid gray */
545 	fill_rect (tb, x + 1, 2, width - (TEXTPAD * 2) - 1, WINHEIGHT - 4);
546 
547 	set_foreground (4);			  /* darkest gray */
548 	draw_line (tb, x, 2, x + width - (TEXTPAD * 2) - 1, 2);
549 	draw_line (tb, x, 2, x, WINHEIGHT - 2);
550 
551 	set_foreground (5);
552 	XDrawString (dd, tb->win, fore_gc, x + TEXTPAD - 1, text_y,
553 						time_str, 5);
554 }
555 
draw_dot(Window win,int x,int y)556 void draw_dot (Window win, int x, int y)
557 {
558 	set_foreground (4);
559 	XDrawPoint (dd, win, fore_gc, x, y);
560 	set_foreground (3);
561 	XDrawPoint (dd, win, fore_gc, x + 1, y + 1);
562 }
563 
draw_grill(Window win,int x)564 void draw_grill (Window win, int x)
565 {
566 	int y = 0;
567 	while (y < WINHEIGHT - 4)
568 	{
569 		y += 3;
570 		draw_dot (win, x + 3, y);
571 		draw_dot (win, x, y);
572 	}
573 }
574 
gui_draw_pager(taskbar * tb)575 void gui_draw_pager (taskbar * tb)
576 {
577 	int i, loc, text_x;
578 	unsigned long *data;
579 	char label[2];
580 
581 	/* try unified window spec first */
582 	data= get_prop_data(root_win, atom__NET_NUMBER_OF_DESKTOPS, XA_CARDINAL, 0);
583 
584 	if (!data)
585 	/* failed, let's try gnome */
586 		data= get_prop_data(root_win, atom__WIN_WORKSPACE_COUNT, XA_CARDINAL, 0);
587 
588   if (data)
589     {
590       register unsigned long max_desks = *data;
591       XFree(data);
592       pager_width = PAGER_BOX_WIDTH * max_desks;
593       for (i=0; i < max_desks; i++)
594 	{
595 	  loc = 8 + (i * PAGER_BOX_WIDTH);
596 	  label[0] = i + '0';
597 	  text_x = loc + (PAGER_BOX_WIDTH / 2) - (XTextWidth(xfs,label,1)/2);
598 	  if (i == tb->my_desktop)
599 	{
600 		set_foreground(1);
601 	      fill_rect (tb, loc + 3, 2, PAGER_BOX_WIDTH - 5, WINHEIGHT - 3 );
602 			set_foreground (4);
603 			draw_line (tb, loc + 2, 1, loc + 2, WINHEIGHT - 2 );
604 			draw_line (tb, loc + 3, 1, loc + PAGER_BOX_WIDTH - 2, 1 );
605 			set_foreground (3);
606 			draw_line (tb, loc + 3, WINHEIGHT - 2 , loc + PAGER_BOX_WIDTH - 2, WINHEIGHT - 2 );
607 			draw_line (tb, loc + PAGER_BOX_WIDTH - 2, WINHEIGHT - 3, loc + PAGER_BOX_WIDTH - 2, 2 );
608 	      set_foreground(5);
609 	      XDrawString(dd, tb->win, fore_gc, text_x, text_y, label, 1);
610 	    }
611 	  else
612 	    {
613 	      set_foreground(0);
614 	      fill_rect (tb, loc + 2, 1, PAGER_BOX_WIDTH - 3, WINHEIGHT - 2 );
615 	      set_foreground(5);
616 	      XDrawString(dd, tb->win, fore_gc, text_x, text_y, label, 1);
617 	    }
618 	  gui_draw_vline(tb, loc + PAGER_BOX_WIDTH);
619 	}
620     }
621   else
622     {
623       pager_width = 0;
624     }
625  }
626 
gui_draw_taskbar(taskbar * tb)627 void gui_draw_taskbar (taskbar * tb)
628 {
629 	task *tk;
630 	int x, width, taskw;
631 	int under = 0;
632 
633 	set_foreground (5);	/* black */
634 	gui_draw_pager(tb);
635 
636 	width = WINWIDTH - pager_width - 8 - time_width - (TEXTPAD * 4);
637 	x = pager_width + 12;
638 
639 	if (tb->num_tasks == 0)
640 		goto clear;
641 
642 	taskw = width / tb->num_tasks;
643 	if (taskw > MAX_TASK_WIDTH)
644 	{
645 		taskw = MAX_TASK_WIDTH;
646 		under = 1;
647 	}
648 
649 	tk = tb->task_list;
650 	while (tk)
651 	{
652 		tk->pos_x = x;
653 		tk->width = taskw - 1;
654 		gui_draw_task (tb, tk);
655 		x += taskw;
656 		tk = tk->next;
657 	}
658 
659 	if (under)
660 	{
661 clear:
662 		gui_draw_vline (tb, x);
663 		set_foreground (0);
664 		fill_rect (tb, x + 2, 0, WINWIDTH, WINHEIGHT);
665 	}
666 
667 	gui_draw_clock (tb);
668 
669 	/*gui_draw_vline (tb, 8);
670 	gui_draw_vline (tb, 74);*/
671 
672 	gui_draw_vline (tb, 8);
673 	gui_draw_vline (tb, pager_width + 12);
674 
675 	draw_grill (tb->win, 2);
676 	draw_grill (tb->win, WINWIDTH - 6);
677 }
678 
679 /**************************************/
680 /* CODE ABOVE DOES GRAPHICS OPERATIONS*/
681 /**************************************/
682 
gui_create_taskbar(void)683 taskbar *gui_create_taskbar (void)
684 {
685 	taskbar *tb;
686 	MWMHints mwm;
687 	XSizeHints size_hints;
688 	XWMHints wmhints;
689 	XSetWindowAttributes att;
690 
691 	att.background_pixel = palette[0];
692 	att.event_mask = ButtonPressMask | ExposureMask;
693 
694 	win = XCreateWindow (
695 				/* display */ dd,
696 				/* parent  */ root_win,
697 				/* x       */ 0,
698 				/* y       */ scr_height - WINHEIGHT,
699 				/* width   */ WINWIDTH,
700 				/* height  */ WINHEIGHT,
701 				/* border  */ 0,
702 				/* depth   */ CopyFromParent,
703 				/* class   */ InputOutput,
704 				/* visual  */ CopyFromParent,
705 				/*value mask*/ CWBackPixel | CWEventMask,
706 				/* attribs */ &att);
707 
708 
709 	set_bottom_strut();
710 
711 	/* don't let any windows cover fspanel */
712 	set_prop (win, atom__WIN_LAYER, 10);	/* WIN_LAYER_ABOVE_DOCK */
713 
714 	set_prop (win, atom__WIN_STATE, WIN_STATE_STICKY |
715 				 WIN_STATE_FIXED_POSITION);
716 
717 	set_prop (win, atom__NET_WM_STATE_STICKY, 0);
718 
719 	set_prop (win, atom__WIN_HINTS, WIN_HINTS_SKIP_FOCUS |
720 				 WIN_HINTS_SKIP_WINLIST |
721 				 WIN_HINTS_SKIP_TASKBAR | WIN_HINTS_DO_NOT_COVER);
722 
723 	set_prop (win, atom__NET_WM_STATE_SKIP_TASKBAR, 0);
724 	set_prop (win, atom__NET_WM_STATE_SKIP_PAGER, 0);
725 
726 	/* borderless motif hint */
727 	memset (&mwm, 0, sizeof (mwm));
728 	mwm.flags = MWM_HINTS_DECORATIONS;
729 	XChangeProperty (dd, win, atom__MOTIF_WM_HINTS,
730 							atom__MOTIF_WM_HINTS, 32, PropModeReplace,
731 							(unsigned char *) &mwm, sizeof (MWMHints) / 4);
732 
733 	/* make sure the WM obeys our window position */
734 	size_hints.flags = PPosition;
735 	/*XSetWMNormalHints (dd, win, &size_hints);*/
736 	XChangeProperty (dd, win, XA_WM_NORMAL_HINTS,
737 							XA_WM_SIZE_HINTS, 32, PropModeReplace,
738 							(unsigned char *) &size_hints, sizeof (XSizeHints) / 4);
739 
740 	/* make our window unfocusable */
741 	wmhints.flags = InputHint;
742 	wmhints.input = 0;
743 	/*XSetWMHints (dd, win, &wmhints);*/
744 	XChangeProperty (dd, win, XA_WM_HINTS,
745 							XA_WM_HINTS, 32, PropModeReplace,
746 							(unsigned char *) &wmhints, sizeof (XWMHints) / 4);
747 
748 	XMapWindow (dd, win);
749 
750 	tb = (taskbar *)calloc (1, sizeof (taskbar));
751 	tb->win = win;
752 
753 	return tb;
754 }
755 
add_task(taskbar * tb,Window win,int focus)756 void add_task (taskbar * tb, Window win, int focus)
757 {
758 	task *tk, *list;
759 
760 	/* is this window on a different desktop? */
761 	if (tb->my_desktop != find_desktop (win) || is_hidden (win))
762 		return;
763 
764 	tk = (task *)calloc (1, sizeof (task));
765 	tk->win = win;
766 	tk->focused = focus;
767 	tk->name = (char *)(void *)get_prop_data (win, XA_WM_NAME, XA_STRING, 0);
768 	tk->iconified = is_iconified (win);
769 
770 	get_task_kdeicon (tk);
771 	if (tk->icon == None)
772 		get_task_hinticon (tk);
773 
774 	XSelectInput (dd, win, PropertyChangeMask | FocusChangeMask |
775 					  StructureNotifyMask);
776 
777 	/* now append it to our linked list */
778 	tb->num_tasks++;
779 
780 	list = tb->task_list;
781 	if (!list)
782 	{
783 		tb->task_list = tk;
784 		return;
785 	}
786 	while (1)
787 	{
788 		if (!list->next)
789 		{
790 			list->next = tk;
791 			return;
792 		}
793 		list = list->next;
794 	}
795 }
796 
797 
move_taskbar(taskbar * tb)798 void move_taskbar (taskbar * tb)
799 {
800 	int x, y;
801 
802 	x = y = 0;
803 
804 	if (tb->hidden)
805 		x = WINWIDTH - TEXTPAD;
806 
807 	if (!tb->at_top)
808 		y = scr_height - WINHEIGHT;
809 
810 	XMoveWindow (dd, tb->win, x, y);
811 }
812 
toggle_placement(taskbar * tb)813 void toggle_placement(taskbar * tb)
814 {
815 	if (tb->hidden)
816 		tb->hidden = 0;
817 	else
818 	{
819 		if(tb->at_top)
820 			set_bottom_strut();
821 		else
822 			set_top_strut();
823 
824 		tb->at_top = !tb->at_top;
825 	}
826 
827 	move_taskbar (tb);
828 }
829 
find_task(taskbar * tb,Window win)830 task * find_task (taskbar * tb, Window win)
831 {
832 	task *list = tb->task_list;
833 	while (list)
834 	{
835 		if (list->win == win)
836 			return list;
837 		list = list->next;
838 	}
839 	return 0;
840 }
841 
del_task(taskbar * tb,Window win)842 void del_task (taskbar * tb, Window win)
843 {
844 	task *next, *prev = 0, *list = tb->task_list;
845 
846 	while (list)
847 	{
848 		next = list->next;
849 		if (list->win == win)
850 		{
851 			/* unlink and free this task */
852 			tb->num_tasks--;
853 			if (list->icon_copied)
854 			{
855 				XFreePixmap (dd, list->icon);
856 				if (list->mask != None)
857 					XFreePixmap (dd, list->mask);
858 			}
859 			if (list->name)
860 				XFree (list->name);
861 			free (list);
862 			if (prev == 0)
863 				tb->task_list = next;
864 			else
865 				prev->next = next;
866 			return;
867 		}
868 		prev = list;
869 		list = next;
870 	}
871 }
872 
taskbar_read_clientlist(taskbar * tb)873 void taskbar_read_clientlist (taskbar * tb)
874 {
875 	Window *win, focus_win;
876 	int num, i, rev, desk, new_desk = 0;
877 	task *list, *next;
878 
879 	desk = find_desktop (root_win);
880 	if (desk != tb->my_desktop)
881 	{
882 		new_desk = 1;
883 		tb->my_desktop = desk;
884 	}
885 
886 	XGetInputFocus (dd, &focus_win, &rev);
887 
888 	/* try unified window spec first */
889 	win = get_prop_data (root_win, atom__NET_CLIENT_LIST, XA_WINDOW, &num);
890 	if (!win)
891 	{
892 		/* failed, let's try gnome */
893 		win = get_prop_data (root_win, atom__WIN_CLIENT_LIST, XA_CARDINAL, &num);
894 		if (!win)
895 			return;
896 	}
897 
898 	/* remove windows that aren't in the _WIN_CLIENT_LIST anymore */
899 	list = tb->task_list;
900 	while (list)
901 	{
902 		list->focused = (focus_win == list->win);
903 		next = list->next;
904 
905 /*
906 		if (!new_desk)
907 			for (i = num - 1; i >= 0; i--)
908 				if (list->win == win[i])
909 					goto dontdel;
910 		del_task (tb, list->win);
911 dontdel:
912 */
913 
914 		if (!new_desk)
915 			for (i = num - 1; i >= 0; i--)
916 				if (list->win == win[i])
917 					goto second_check;
918 		del_task (tb, list->win);
919 second_check:
920 		/* Frank Hale <frankhale@yahoo.com>
921 		 * --------------------------------
922 		 * 29 July 2001
923 		 *
924 		 * Well what if the window was changed to another desktop? We need to get rid of it.
925 		 * Otherwise our window manager needs to switch to that desktop in order to get fspanel
926 		 * to update its task list properly.
927 		 *
928 		 */
929 		if (tb->my_desktop != find_desktop (list->win) || is_hidden (list->win))
930 			del_task(tb, list->win);
931 
932 		list = next;
933 	}
934 
935 	/* add any new windows */
936 	for (i = 0; i < num; i++)
937 	{
938 		if (!find_task (tb, win[i]))
939 			add_task (tb, win[i], (win[i] == focus_win));
940 	}
941 
942 	XFree (win);
943 }
944 
handle_press(taskbar * tb,int x,int y)945 void handle_press (taskbar * tb, int x, int y)
946 {
947 	task *tk;
948 
949 	/* clicked on pager */
950 	if (x > 8 && x < pager_width + 8)
951 	{
952 	    switch_desk (tb, ((x-8) /PAGER_BOX_WIDTH) - tb->my_desktop);
953 	}
954 
955 	/* clicked left grill */
956 	if (x < 6)
957 	{
958 		toggle_placement(tb);
959 
960 		return;
961 	}
962 
963 	/* clicked right grill */
964 	if (x + TEXTPAD > WINWIDTH)
965 	{
966 		tb->hidden = !tb->hidden;
967 		move_taskbar (tb);
968 		return;
969 	}
970 
971 	tk = tb->task_list;
972 	while (tk)
973 	{
974 		if (x > tk->pos_x && x < tk->pos_x + tk->width)
975 		{
976 			if (tk->iconified)
977 			{
978 				tk->iconified = 0;
979 				tk->focused = 1;
980 				XMapWindow (dd, tk->win);
981 			} else
982 			{
983 				if (tk->focused)
984 				{
985 					tk->iconified = 1;
986 					tk->focused = 0;
987 					XIconifyWindow (dd, tk->win, scr_screen);
988 				} else
989 				{
990 					tk->focused = 1;
991 					XRaiseWindow (dd, tk->win);
992 					XSetInputFocus (dd, tk->win, RevertToNone, CurrentTime);
993 				}
994 			}
995 
996 			XSync (dd, False);
997 
998 			gui_draw_task (tb, tk);
999 		} else
1000 		{
1001 			if (tk->focused)
1002 			{
1003 				tk->focused = 0;
1004 				gui_draw_task (tb, tk);
1005 			}
1006 		}
1007 
1008 		tk = tk->next;
1009 	}
1010 }
1011 
handle_focusin(taskbar * tb,Window win)1012 void handle_focusin (taskbar * tb, Window win)
1013 {
1014 	task *tk;
1015 
1016 	tk = tb->task_list;
1017 	while (tk)
1018 	{
1019 		if (tk->focused)
1020 		{
1021 			if (tk->win != win)
1022 			{
1023 				tk->focused = 0;
1024 				gui_draw_task (tb, tk);
1025 			}
1026 		} else
1027 		{
1028 			if (tk->win == win)
1029 			{
1030 				tk->focused = 1;
1031 				gui_draw_task (tb, tk);
1032 			}
1033 		}
1034 		tk = tk->next;
1035 	}
1036 }
1037 
handle_propertynotify(taskbar * tb,Window win,Atom at)1038 void handle_propertynotify (taskbar * tb, Window win, Atom at)
1039 {
1040 	task *tk;
1041 
1042 	/*
1043 	 * Hmm, the next if statement doesn't correctly
1044 	 * Update the taskbar when a window changes desktops.
1045 	 * The next 2 lines simply by pass whatever it was
1046 	 * the author was intending so that the taskbar gets
1047 	 * updated no matter what. Its a hack, probably not
1048 	 * optimal but I don't give a shit.
1049 	 *
1050 	 * Frank Hale
1051 	 */
1052 	taskbar_read_clientlist (tb);
1053 	gui_draw_taskbar (tb);
1054 
1055 	/*
1056 	if (win == root_win)
1057 	{
1058 		if (at == atom__NET_CLIENT_LIST ||
1059 			 at == atom__WIN_CLIENT_LIST ||
1060 			 at == atom__NET_CURRENT_DESKTOP ||
1061 			 at == atom__WIN_WORKSPACE)
1062 		{
1063 			taskbar_read_clientlist (tb);
1064 			gui_draw_taskbar (tb);
1065 		}
1066 		return;
1067 	}
1068 	*/
1069 
1070 	tk = find_task (tb, win);
1071 	if (!tk)
1072 		return;
1073 
1074 	if (at == XA_WM_NAME)
1075 	{
1076 		/* window's title changed */
1077 		if (tk->name)
1078 			XFree (tk->name);
1079 		tk->name = (char *)(void *)get_prop_data (tk->win, XA_WM_NAME, XA_STRING, 0);
1080 		gui_draw_task (tb, tk);
1081 	} else if (at == atom_WM_STATE)
1082 	{
1083 		/* iconified state changed? */
1084 		if (is_iconified (tk->win) != tk->iconified)
1085 		{
1086 			tk->iconified = !tk->iconified;
1087 			gui_draw_task (tb, tk);
1088 		}
1089 	} else if (at == XA_WM_HINTS)
1090 	{
1091 		/* some windows set their WM_HINTS icon after mapping */
1092 		if (tk->icon == generic_icon)
1093 		{
1094 			get_task_hinticon (tk);
1095 			gui_draw_task (tb, tk);
1096 		}
1097 	}
1098 }
1099 
handle_error(Display * d,XErrorEvent * ev)1100 void handle_error (Display * d, XErrorEvent * ev)
1101 {
1102 }
1103 
main(int argc,char * argv[])1104 int main (int argc, char *argv[])
1105 {
1106 	taskbar *tb;
1107 	XEvent ev;
1108 	fd_set fd;
1109 	struct timeval tv;
1110 	int xfd;
1111 	time_t now;
1112 	struct tm *lt;
1113 	int i;
1114 	char *placement_str="";
1115 	int placement=0;
1116 
1117 /* Macro borrowed from aewm's code. */
1118 #define OPT_STR(name, variable)                                      \
1119     if (strcmp(argv[i], name) == 0 && i+1<argc) {                    \
1120         variable = argv[++i];                                        \
1121         continue;                                                    \
1122     }
1123 
1124 	for (i = 1; i < argc; i++)
1125     	{
1126 		OPT_STR("-placement", placement_str)
1127 
1128 		if(strcmp(argv[i], "-usage")==0) {
1129         		fprintf(stderr, "usage: fspanel [options]\n");
1130 	        	fprintf(stderr, "   options are: -placement (top|bottom), -usage\n");
1131 			return 0;
1132 		}
1133 	}
1134 
1135 	// either top or bottom (default: bottom)
1136 	if (strcmp(placement_str, "top")==0)
1137 		placement = 1;
1138 
1139 	dd = XOpenDisplay (NULL);
1140 	if (!dd)
1141 		return 0;
1142 
1143 	scr_screen = DefaultScreen (dd);
1144 	scr_depth = DefaultDepth (dd, scr_screen);
1145 	scr_height = DisplayHeight (dd, scr_screen);
1146 	scr_width = DisplayWidth (dd, scr_screen);
1147 	root_win = RootWindow (dd, scr_screen);
1148 
1149 	/* helps us catch windows closing/opening */
1150 	XSelectInput (dd, root_win, PropertyChangeMask);
1151 
1152 	XSetErrorHandler ((XErrorHandler) handle_error);
1153 
1154 	XInternAtoms (dd, atom_names, ATOM_COUNT, False, atoms);
1155 
1156 	net_wm_strut = XInternAtom (dd, "_NET_WM_STRUT", False);
1157 
1158 	gui_init ();
1159 	tb = gui_create_taskbar ();
1160 	xfd = ConnectionNumber (dd);
1161 
1162 	XSync (dd, False);
1163 
1164 	// if not top or bottom, the placement is off. virtual ducttape.
1165 	toggle_placement(tb);
1166 	toggle_placement(tb);
1167 
1168 	if(placement)
1169 	{
1170 		toggle_placement(tb);
1171 	}
1172 
1173 	while (1)
1174 	{
1175 		now = time (0);
1176 		lt = gmtime (&now);
1177 		tv.tv_usec = 0;
1178 		tv.tv_sec = 60 - lt->tm_sec;
1179 		FD_ZERO (&fd);
1180 		FD_SET (xfd, &fd);
1181 		if (select (xfd + 1, &fd, 0, 0, &tv) == 0)
1182 			gui_draw_clock (tb);
1183 
1184 		while (XPending (dd))
1185 		{
1186 			XNextEvent (dd, &ev);
1187 
1188 			switch (ev.type)
1189 			{
1190 			case ButtonPress:
1191 				if (ev.xbutton.button == 1)
1192 					handle_press (tb, ev.xbutton.x, ev.xbutton.y);
1193 				break;
1194 			case DestroyNotify:
1195 				del_task (tb, ev.xdestroywindow.window);
1196 				/* fall through */
1197 			case Expose:
1198 				gui_draw_taskbar (tb);
1199 				break;
1200 			case PropertyNotify:
1201 				handle_propertynotify (tb, ev.xproperty.window, ev.xproperty.atom);
1202 				break;
1203 			case FocusIn:
1204 				handle_focusin (tb, ev.xfocus.window);
1205 				break;
1206 
1207 			}
1208 		}
1209 	}
1210 
1211 	/*XCloseDisplay (dd);
1212 
1213    return 0;*/
1214 }
1215