]> Dogcows Code - chaz/tint2/blob - src/taskbar/task.c
desktop name : last step
[chaz/tint2] / src / taskbar / task.c
1 /**************************************************************************
2 *
3 * Tint2 : task
4 *
5 * Copyright (C) 2007 Pål Staurland (staura@gmail.com)
6 * Modified (C) 2008 thierry lorthiois (lorthiois@bbsoft.fr) from Omega distribution
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License version 2
10 * as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 **************************************************************************/
20
21 #include <X11/Xlib.h>
22 #include <X11/Xutil.h>
23 #include <X11/Xatom.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <glib.h>
28 #include <unistd.h>
29
30 #include "window.h"
31 #include "task.h"
32 #include "taskbar.h"
33 #include "server.h"
34 #include "panel.h"
35 #include "tooltip.h"
36 #include "timer.h"
37
38 timeout* urgent_timeout;
39 GSList* urgent_list;
40
41 const char* task_get_tooltip(void* obj)
42 {
43 Task* t = obj;
44 return t->title;
45 }
46
47
48 Task *add_task (Window win)
49 {
50 if (!win) return 0;
51 if (window_is_hidden(win)) return 0;
52
53 int monitor;
54 if (nb_panel > 1) {
55 monitor = window_get_monitor (win);
56 if (monitor >= nb_panel) monitor = 0;
57 }
58 else monitor = 0;
59
60 Task new_tsk;
61 new_tsk.win = win;
62 new_tsk.desktop = window_get_desktop (win);
63 new_tsk.area.panel = &panel1[monitor];
64 new_tsk.current_state = window_is_iconified(win) ? TASK_ICONIFIED : TASK_NORMAL;
65
66 // allocate only one title and one icon
67 // even with task_on_all_desktop and with task_on_all_panel
68 new_tsk.title = 0;
69 int k;
70 for (k=0; k<TASK_STATE_COUNT; ++k) {
71 new_tsk.icon[k] = 0;
72 new_tsk.state_pix[k] = 0;
73 }
74 get_title(&new_tsk);
75 get_icon(&new_tsk);
76
77 //printf("task %s : desktop %d, monitor %d\n", new_tsk->title, desktop, monitor);
78 XSelectInput (server.dsp, new_tsk.win, PropertyChangeMask|StructureNotifyMask);
79
80 GPtrArray* task_group = g_ptr_array_new();
81 Taskbar *tskbar;
82 Task *new_tsk2=0;
83 int j;
84 for (j=0 ; j < panel1[monitor].nb_desktop ; j++) {
85 if (new_tsk.desktop != ALLDESKTOP && new_tsk.desktop != j) continue;
86
87 tskbar = &panel1[monitor].taskbar[j];
88 new_tsk2 = malloc(sizeof(Task));
89 memcpy(&new_tsk2->area, &panel1[monitor].g_task.area, sizeof(Area));
90 new_tsk2->area.parent = tskbar;
91 new_tsk2->win = new_tsk.win;
92 new_tsk2->desktop = new_tsk.desktop;
93 new_tsk2->current_state = -1; // to update the current state later in set_task_state...
94 if (new_tsk2->desktop == ALLDESKTOP && server.desktop != j) {
95 // hide ALLDESKTOP task on non-current desktop
96 new_tsk2->area.on_screen = 0;
97 }
98 new_tsk2->title = new_tsk.title;
99 new_tsk2->area._get_tooltip_text = task_get_tooltip;
100 for (k=0; k<TASK_STATE_COUNT; ++k) {
101 new_tsk2->icon[k] = new_tsk.icon[k];
102 new_tsk2->state_pix[k] = 0;
103 }
104 new_tsk2->icon_width = new_tsk.icon_width;
105 new_tsk2->icon_height = new_tsk.icon_height;
106 tskbar->area.list = g_slist_append(tskbar->area.list, new_tsk2);
107 tskbar->area.resize = 1;
108 g_ptr_array_add(task_group, new_tsk2);
109 //printf("add_task panel %d, desktop %d, task %s\n", i, j, new_tsk2->title);
110 }
111 Window* key = malloc(sizeof(Window));
112 *key = new_tsk.win;
113 g_hash_table_insert(win_to_task_table, key, task_group);
114 set_task_state(new_tsk2, new_tsk.current_state);
115
116 if (window_is_urgent(win))
117 add_urgent(new_tsk2);
118
119 return new_tsk2;
120 }
121
122
123 void remove_task (Task *tsk)
124 {
125 if (!tsk) return;
126
127 Window win = tsk->win;
128
129 // free title and icon just for the first task
130 // even with task_on_all_desktop and with task_on_all_panel
131 //printf("remove_task %s %d\n", tsk->title, tsk->desktop);
132 if (tsk->title)
133 free (tsk->title);
134 int k;
135 for (k=0; k<TASK_STATE_COUNT; ++k) {
136 if (tsk->icon[k]) {
137 imlib_context_set_image(tsk->icon[k]);
138 imlib_free_image();
139 tsk->icon[k] = 0;
140 if (tsk->state_pix[k]) XFreePixmap(server.dsp, tsk->state_pix[k]);
141 }
142 }
143
144 int i;
145 Task *tsk2;
146 Taskbar *tskbar;
147 GPtrArray* task_group = g_hash_table_lookup(win_to_task_table, &win);
148 for (i=0; i<task_group->len; ++i) {
149 tsk2 = g_ptr_array_index(task_group, i);
150 tskbar = tsk2->area.parent;
151 tskbar->area.list = g_slist_remove(tskbar->area.list, tsk2);
152 tskbar->area.resize = 1;
153 if (tsk2 == task_active) task_active = 0;
154 if (tsk2 == task_drag) task_drag = 0;
155 if (g_slist_find(urgent_list, tsk2)) del_urgent(tsk2);
156 free(tsk2);
157 }
158 g_hash_table_remove(win_to_task_table, &win);
159 }
160
161
162 int get_title(Task *tsk)
163 {
164 Panel *panel = tsk->area.panel;
165 char *title, *name;
166
167 if (!panel->g_task.text && !g_tooltip.enabled) return 0;
168
169 name = server_get_property (tsk->win, server.atom._NET_WM_VISIBLE_NAME, server.atom.UTF8_STRING, 0);
170 if (!name || !strlen(name)) {
171 name = server_get_property (tsk->win, server.atom._NET_WM_NAME, server.atom.UTF8_STRING, 0);
172 if (!name || !strlen(name)) {
173 name = server_get_property (tsk->win, server.atom.WM_NAME, XA_STRING, 0);
174 if (!name || !strlen(name)) {
175 name = malloc(10);
176 strcpy(name, "Untitled");
177 }
178 }
179 }
180
181 // add space before title
182 title = malloc(strlen(name)+2);
183 if (panel->g_task.icon) strcpy(title, " ");
184 else title[0] = 0;
185 strcat(title, name);
186 if (name) XFree (name);
187
188 if (tsk->title) {
189 // check unecessary title change
190 if (strcmp(tsk->title, title) == 0) {
191 free(title);
192 return 0;
193 }
194 else
195 free(tsk->title);
196 }
197
198 tsk->title = title;
199 GPtrArray* task_group = task_get_tasks(tsk->win);
200 if (task_group) {
201 int i;
202 for (i=0; i<task_group->len; ++i) {
203 Task* tsk2 = g_ptr_array_index(task_group, i);
204 tsk2->title = tsk->title;
205 set_task_redraw(tsk2);
206 }
207 }
208 return 1;
209 }
210
211
212 void get_icon (Task *tsk)
213 {
214 Panel *panel = tsk->area.panel;
215 if (!panel->g_task.icon) return;
216 int i;
217 Imlib_Image img = NULL;
218 XWMHints *hints = 0;
219 gulong *data = 0;
220
221 int k;
222 for (k=0; k<TASK_STATE_COUNT; ++k) {
223 if (tsk->icon[k]) {
224 imlib_context_set_image(tsk->icon[k]);
225 imlib_free_image();
226 tsk->icon[k] = 0;
227 }
228 }
229
230 data = server_get_property (tsk->win, server.atom._NET_WM_ICON, XA_CARDINAL, &i);
231 if (data) {
232 // get ARGB icon
233 int w, h;
234 gulong *tmp_data;
235
236 tmp_data = get_best_icon (data, get_icon_count (data, i), i, &w, &h, panel->g_task.icon_size1);
237 #ifdef __x86_64__
238 DATA32 icon_data[w * h];
239 int length = w * h;
240 for (i = 0; i < length; ++i)
241 icon_data[i] = tmp_data[i];
242 img = imlib_create_image_using_copied_data (w, h, icon_data);
243 #else
244 img = imlib_create_image_using_data (w, h, (DATA32*)tmp_data);
245 #endif
246 }
247 else {
248 // get Pixmap icon
249 hints = XGetWMHints(server.dsp, tsk->win);
250 if (hints) {
251 if (hints->flags & IconPixmapHint && hints->icon_pixmap != 0) {
252 // get width, height and depth for the pixmap
253 Window root;
254 int icon_x, icon_y;
255 uint border_width, bpp;
256 uint w, h;
257
258 //printf(" get pixmap\n");
259 XGetGeometry(server.dsp, hints->icon_pixmap, &root, &icon_x, &icon_y, &w, &h, &border_width, &bpp);
260 imlib_context_set_drawable(hints->icon_pixmap);
261 img = imlib_create_image_from_drawable(hints->icon_mask, 0, 0, w, h, 0);
262 }
263 }
264 }
265 if (img == NULL) {
266 imlib_context_set_image(default_icon);
267 img = imlib_clone_image();
268 }
269
270 // transform icons
271 imlib_context_set_image(img);
272 imlib_image_set_has_alpha(1);
273 int w, h;
274 w = imlib_image_get_width();
275 h = imlib_image_get_height();
276 Imlib_Image orig_image = imlib_create_cropped_scaled_image(0, 0, w, h, panel->g_task.icon_size1, panel->g_task.icon_size1);
277 imlib_free_image();
278
279 imlib_context_set_image(orig_image);
280 tsk->icon_width = imlib_image_get_width();
281 tsk->icon_height = imlib_image_get_height();
282 for (k=0; k<TASK_STATE_COUNT; ++k) {
283 imlib_context_set_image(orig_image);
284 tsk->icon[k] = imlib_clone_image();
285 imlib_context_set_image(tsk->icon[k]);
286 DATA32 *data32;
287 if (panel->g_task.alpha[k] != 100 || panel->g_task.saturation[k] != 0 || panel->g_task.brightness[k] != 0) {
288 data32 = imlib_image_get_data();
289 adjust_asb(data32, tsk->icon_width, tsk->icon_height, panel->g_task.alpha[k], (float)panel->g_task.saturation[k]/100, (float)panel->g_task.brightness[k]/100);
290 imlib_image_put_back_data(data32);
291 }
292 }
293 imlib_context_set_image(orig_image);
294 imlib_free_image();
295
296 if (hints)
297 XFree(hints);
298 if (data)
299 XFree (data);
300
301 GPtrArray* task_group = task_get_tasks(tsk->win);
302 if (task_group) {
303 for (i=0; i<task_group->len; ++i) {
304 Task* tsk2 = g_ptr_array_index(task_group, i);
305 tsk2->icon_width = tsk->icon_width;
306 tsk2->icon_height = tsk->icon_height;
307 int k;
308 for (k=0; k<TASK_STATE_COUNT; ++k)
309 tsk2->icon[k] = tsk->icon[k];
310 set_task_redraw(tsk2);
311 }
312 }
313 }
314
315
316 void draw_task_icon (Task *tsk, int text_width)
317 {
318 if (tsk->icon[tsk->current_state] == 0) return;
319
320 // Find pos
321 int pos_x;
322 Panel *panel = (Panel*)tsk->area.panel;
323 if (panel->g_task.centered) {
324 if (panel->g_task.text)
325 pos_x = (tsk->area.width - text_width - panel->g_task.icon_size1) / 2;
326 else
327 pos_x = (tsk->area.width - panel->g_task.icon_size1) / 2;
328 }
329 else pos_x = panel->g_task.area.paddingxlr + tsk->area.bg->border.width;
330
331 // Render
332 imlib_context_set_image (tsk->icon[tsk->current_state]);
333 if (server.real_transparency) {
334 render_image(tsk->area.pix, pos_x, panel->g_task.icon_posy, imlib_image_get_width(), imlib_image_get_height() );
335 }
336 else {
337 imlib_context_set_drawable(tsk->area.pix);
338 imlib_render_image_on_drawable (pos_x, panel->g_task.icon_posy);
339 }
340 }
341
342
343 void draw_task (void *obj, cairo_t *c)
344 {
345 Task *tsk = obj;
346 tsk->state_pix[tsk->current_state] = tsk->area.pix;
347 PangoLayout *layout;
348 Color *config_text;
349 int width=0, height;
350 Panel *panel = (Panel*)tsk->area.panel;
351 //printf("draw_task %d %d\n", tsk->area.posx, tsk->area.posy);
352
353 if (panel->g_task.text) {
354 /* Layout */
355 layout = pango_cairo_create_layout (c);
356 pango_layout_set_font_description (layout, panel->g_task.font_desc);
357 pango_layout_set_text(layout, tsk->title, -1);
358
359 /* Drawing width and Cut text */
360 // pango use U+22EF or U+2026
361 pango_layout_set_width(layout, ((Taskbar*)tsk->area.parent)->text_width * PANGO_SCALE);
362 pango_layout_set_height(layout, panel->g_task.text_height * PANGO_SCALE);
363 pango_layout_set_wrap(layout, PANGO_WRAP_CHAR);
364 pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
365
366 /* Center text */
367 if (panel->g_task.centered) pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
368 else pango_layout_set_alignment (layout, PANGO_ALIGN_LEFT);
369
370 pango_layout_get_pixel_size (layout, &width, &height);
371
372 config_text = &panel->g_task.font[tsk->current_state];
373 cairo_set_source_rgba (c, config_text->color[0], config_text->color[1], config_text->color[2], config_text->alpha);
374
375 pango_cairo_update_layout (c, layout);
376 double text_posy = (panel->g_task.area.height - height) / 2.0;
377 cairo_move_to (c, panel->g_task.text_posx, text_posy);
378 pango_cairo_show_layout (c, layout);
379
380 if (panel->g_task.font_shadow) {
381 cairo_set_source_rgba (c, 0.0, 0.0, 0.0, 0.5);
382 pango_cairo_update_layout (c, layout);
383 cairo_move_to (c, panel->g_task.text_posx + 1, text_posy + 1);
384 pango_cairo_show_layout (c, layout);
385 }
386 g_object_unref (layout);
387 }
388
389 if (panel->g_task.icon) {
390 draw_task_icon (tsk, width);
391 }
392 }
393
394
395 void on_change_task (void *obj)
396 {
397 Task *tsk = obj;
398 Panel *panel = (Panel*)tsk->area.panel;
399
400 long value[] = { panel->posx+tsk->area.posx, panel->posy+tsk->area.posy, tsk->area.width, tsk->area.height };
401 XChangeProperty (server.dsp, tsk->win, server.atom._NET_WM_ICON_GEOMETRY, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)value, 4);
402
403 // reset Pixmap when position/size changed
404 set_task_redraw(tsk);
405 }
406
407
408 Task *next_task(Task *tsk)
409 {
410 if (tsk == 0)
411 return 0;
412
413 GSList *l0;
414 Task *tsk1;
415 Taskbar* tskbar = tsk->area.parent;
416
417 l0 = tskbar->area.list;
418 if (taskbarname_enabled) l0 = l0->next;
419 for (; l0 ; l0 = l0->next) {
420 tsk1 = l0->data;
421 if (tsk1 == tsk) {
422 if (l0->next == 0) l0 = tskbar->area.list;
423 else l0 = l0->next;
424 return l0->data;
425 }
426 }
427
428 return 0;
429 }
430
431 Task *prev_task(Task *tsk)
432 {
433 if (tsk == 0)
434 return 0;
435
436 GSList *l0;
437 Task *tsk1, *tsk2;
438 Taskbar* tskbar = tsk->area.parent;
439
440 tsk2 = 0;
441 l0 = tskbar->area.list;
442 if (taskbarname_enabled) l0 = l0->next;
443 for (; l0 ; l0 = l0->next) {
444 tsk1 = l0->data;
445 if (tsk1 == tsk) {
446 if (l0 == tskbar->area.list) {
447 l0 = g_slist_last ( l0 );
448 tsk2 = l0->data;
449 }
450 return tsk2;
451 }
452 tsk2 = tsk1;
453 }
454
455 return 0;
456 }
457
458
459 void active_task()
460 {
461 if (task_active) {
462 set_task_state(task_active, window_is_iconified(task_active->win) ? TASK_ICONIFIED : TASK_NORMAL);
463 task_active = 0;
464 }
465
466 Window w1 = window_get_active();
467 //printf("Change active task %ld\n", w1);
468
469 if (w1) {
470 if (!task_get_tasks(w1)) {
471 Window w2;
472 while (XGetTransientForHint(server.dsp, w1, &w2))
473 w1 = w2;
474 }
475 set_task_state((task_active = task_get_task(w1)), TASK_ACTIVE);
476 }
477 }
478
479
480 void set_task_state(Task *tsk, int state)
481 {
482 if (tsk == 0 || state < 0 || state >= TASK_STATE_COUNT)
483 return;
484
485 if (tsk->current_state != state) {
486 GPtrArray* task_group = task_get_tasks(tsk->win);
487 if (task_group) {
488 int i;
489 for (i=0; i<task_group->len; ++i) {
490 Task* tsk1 = g_ptr_array_index(task_group, i);
491 tsk1->current_state = state;
492 tsk1->area.bg = panel1[0].g_task.background[state];
493 tsk1->area.pix = tsk1->state_pix[state];
494 if (tsk1->state_pix[state] == 0)
495 tsk1->area.redraw = 1;
496 if (state == TASK_ACTIVE && g_slist_find(urgent_list, tsk1))
497 del_urgent(tsk1);
498 }
499 panel_refresh = 1;
500 }
501 }
502 }
503
504
505 void set_task_redraw(Task* tsk)
506 {
507 int k;
508 for (k=0; k<TASK_STATE_COUNT; ++k) {
509 if (tsk->state_pix[k]) XFreePixmap(server.dsp, tsk->state_pix[k]);
510 tsk->state_pix[k] = 0;
511 }
512 tsk->area.pix = 0;
513 tsk->area.redraw = 1;
514 }
515
516
517 void blink_urgent(void* arg)
518 {
519 GSList* urgent_task = urgent_list;
520 while (urgent_task) {
521 Task* t = urgent_task->data;
522 if ( t->urgent_tick < max_tick_urgent) {
523 if (t->urgent_tick++ % 2)
524 set_task_state(t, TASK_URGENT);
525 else
526 set_task_state(t, window_is_iconified(t->win) ? TASK_ICONIFIED : TASK_NORMAL);
527 }
528 urgent_task = urgent_task->next;
529 }
530 panel_refresh = 1;
531 }
532
533
534 void add_urgent(Task *tsk)
535 {
536 if (!tsk)
537 return;
538
539 // some programs set urgency hint although they are active
540 if ( task_active && task_active->win == tsk->win )
541 return;
542
543 tsk = task_get_task(tsk->win); // always add the first tsk for a task group (omnipresent windows)
544 tsk->urgent_tick = 0;
545 if (g_slist_find(urgent_list, tsk))
546 return;
547
548 // not yet in the list, so we have to add it
549 urgent_list = g_slist_prepend(urgent_list, tsk);
550
551 if (urgent_timeout == 0)
552 urgent_timeout = add_timeout(10, 1000, blink_urgent, 0);
553 }
554
555
556 void del_urgent(Task *tsk)
557 {
558 urgent_list = g_slist_remove(urgent_list, tsk);
559 if (urgent_list == 0) {
560 stop_timeout(urgent_timeout);
561 urgent_timeout = 0;
562 }
563 }
This page took 0.059451 seconds and 4 git commands to generate.