]> Dogcows Code - chaz/tint2/blob - src/taskbar/task.c
*change* make tooltip more generous, and preparation for setting a tooltip on the...
[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)
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 "server.h"
33 #include "panel.h"
34 #include "tooltip.h"
35 #include "timer.h"
36
37 static int urgent_timer = 0;
38
39 const char* task_get_tooltip(void* obj)
40 {
41 Task* t = obj;
42 return t->title;
43 }
44
45
46 Task *add_task (Window win)
47 {
48 if (!win) return 0;
49 if (window_is_hidden(win)) return 0;
50
51 int monitor;
52
53 Task new_tsk;
54 new_tsk.win = win;
55 new_tsk.desktop = window_get_desktop (win);
56 if (nb_panel > 1) {
57 monitor = window_get_monitor (win);
58 if (monitor >= nb_panel) monitor = 0;
59 }
60 else monitor = 0;
61 new_tsk.area.panel = &panel1[monitor];
62
63 // allocate only one title and one icon
64 // even with task_on_all_desktop and with task_on_all_panel
65 new_tsk.title = 0;
66 new_tsk.icon = new_tsk.icon_active = NULL;
67 get_title(&new_tsk);
68 get_icon(&new_tsk);
69
70 //printf("task %s : desktop %d, monitor %d\n", new_tsk->title, desktop, monitor);
71 XSelectInput (server.dsp, new_tsk.win, PropertyChangeMask|StructureNotifyMask);
72
73 Taskbar *tskbar;
74 Task *new_tsk2=0;
75 int i, j;
76 for (i=0 ; i < nb_panel ; i++) {
77 for (j=0 ; j < panel1[i].nb_desktop ; j++) {
78 if (new_tsk.desktop != ALLDESKTOP && new_tsk.desktop != j) continue;
79 if (nb_panel > 1 && panel1[i].monitor != monitor) continue;
80
81 tskbar = &panel1[i].taskbar[j];
82 new_tsk2 = malloc(sizeof(Task));
83 memcpy(&new_tsk2->area, &panel1[i].g_task.area, sizeof(Area));
84 new_tsk2->area.parent = tskbar;
85 new_tsk2->win = new_tsk.win;
86 new_tsk2->desktop = new_tsk.desktop;
87 if (new_tsk2->desktop == ALLDESKTOP && server.desktop != j) {
88 // hide ALLDESKTOP task on non-current desktop
89 new_tsk2->area.on_screen = 0;
90 }
91 new_tsk2->title = new_tsk.title;
92 new_tsk2->area._get_tooltip_text = task_get_tooltip;
93 new_tsk2->icon = new_tsk.icon;
94 new_tsk2->icon_active = new_tsk.icon_active;
95 new_tsk2->icon_width = new_tsk.icon_width;
96 new_tsk2->icon_height = new_tsk.icon_height;
97 tskbar->area.list = g_slist_append(tskbar->area.list, new_tsk2);
98 tskbar->area.resize = 1;
99 //printf("add_task panel %d, desktop %d, task %s\n", i, j, new_tsk2->title);
100 }
101 }
102 return new_tsk2;
103 }
104
105
106 void remove_task (Task *tsk)
107 {
108 if (!tsk) return;
109
110 Window win = tsk->win;
111 int desktop = tsk->desktop;
112
113 // free title and icon just for the first task
114 // even with task_on_all_desktop and with task_on_all_panel
115 //printf("remove_task %s %d\n", tsk->title, tsk->desktop);
116 if (tsk->title)
117 free (tsk->title);
118 if (tsk->icon) {
119 imlib_context_set_image(tsk->icon);
120 imlib_free_image();
121 imlib_context_set_image(tsk->icon_active);
122 imlib_free_image();
123 tsk->icon = tsk->icon_active = NULL;
124 }
125
126 int i, j;
127 Task *tsk2;
128 Taskbar *tskbar;
129 for (i=0 ; i < nb_panel ; i++) {
130 for (j=0 ; j < panel1[i].nb_desktop ; j++) {
131 if (desktop != ALLDESKTOP && desktop != j) continue;
132
133 GSList *l0;
134 tskbar = &panel1[i].taskbar[j];
135 for (l0 = tskbar->area.list; l0 ; ) {
136 tsk2 = l0->data;
137 l0 = l0->next;
138 if (win == tsk2->win) {
139 tskbar->area.list = g_slist_remove(tskbar->area.list, tsk2);
140 tskbar->area.resize = 1;
141
142 if (tsk2 == task_active)
143 task_active = 0;
144 if (tsk2 == task_drag)
145 task_drag = 0;
146
147 XFreePixmap (server.dsp, tsk2->area.pix.pmap);
148 XFreePixmap (server.dsp, tsk2->area.pix_active.pmap);
149 free(tsk2);
150 }
151 }
152 }
153 }
154 }
155
156
157 void get_title(Task *tsk)
158 {
159 Panel *panel = tsk->area.panel;
160 char *title, *name;
161
162 if (!panel->g_task.text && !g_tooltip.enabled) return;
163
164 name = server_get_property (tsk->win, server.atom._NET_WM_VISIBLE_NAME, server.atom.UTF8_STRING, 0);
165 if (!name || !strlen(name)) {
166 name = server_get_property (tsk->win, server.atom._NET_WM_NAME, server.atom.UTF8_STRING, 0);
167 if (!name || !strlen(name)) {
168 name = server_get_property (tsk->win, server.atom.WM_NAME, XA_STRING, 0);
169 if (!name || !strlen(name)) {
170 name = malloc(10);
171 strcpy(name, "Untitled");
172 }
173 }
174 }
175
176 // add space before title
177 title = malloc(strlen(name)+2);
178 if (panel->g_task.icon) strcpy(title, " ");
179 else title[0] = 0;
180 strcat(title, name);
181 if (name) XFree (name);
182
183 tsk->area.redraw = 1;
184 if (tsk->title)
185 free(tsk->title);
186 tsk->title = title;
187 }
188
189
190 void get_icon (Task *tsk)
191 {
192 Panel *panel = tsk->area.panel;
193 if (!panel->g_task.icon) return;
194 int i;
195 Imlib_Image img = NULL;
196 XWMHints *hints = 0;
197 long *data = 0;
198
199 if (tsk->icon) {
200 imlib_context_set_image(tsk->icon);
201 imlib_free_image();
202 imlib_context_set_image(tsk->icon_active);
203 imlib_free_image();
204 tsk->icon = tsk->icon_active = NULL;
205 }
206 tsk->area.redraw = 1;
207
208 data = server_get_property (tsk->win, server.atom._NET_WM_ICON, XA_CARDINAL, &i);
209 if (data) {
210 // get ARGB icon
211 int w, h;
212 long *tmp_data;
213
214 tmp_data = get_best_icon (data, get_icon_count (data, i), i, &w, &h, panel->g_task.icon_size1);
215
216 #ifdef __x86_64__
217 DATA32 icon_data[w * h];
218 int length = w * h;
219 for (i = 0; i < length; ++i)
220 icon_data[i] = tmp_data[i];
221 img = imlib_create_image_using_copied_data (w, h, icon_data);
222 #else
223 img = imlib_create_image_using_data (w, h, (DATA32*)tmp_data);
224 #endif
225 }
226 else {
227 // get Pixmap icon
228 hints = XGetWMHints(server.dsp, tsk->win);
229 if (hints) {
230 if (hints->flags & IconPixmapHint && hints->icon_pixmap != 0) {
231 // get width, height and depth for the pixmap
232 Window root;
233 int icon_x, icon_y;
234 uint border_width, bpp;
235 uint w, h;
236
237 //printf(" get pixmap\n");
238 XGetGeometry(server.dsp, hints->icon_pixmap, &root, &icon_x, &icon_y, &w, &h, &border_width, &bpp);
239 imlib_context_set_drawable(hints->icon_pixmap);
240 img = imlib_create_image_from_drawable(hints->icon_mask, 0, 0, w, h, 0);
241 }
242 }
243 }
244 if (img == NULL) {
245 imlib_context_set_image(default_icon);
246 img = imlib_clone_image();
247 }
248
249 // transform icons
250 imlib_context_set_image(img);
251 imlib_image_set_has_alpha(1);
252 int w, h;
253 w = imlib_image_get_width();
254 h = imlib_image_get_height();
255 tsk->icon = imlib_create_cropped_scaled_image(0, 0, w, h, panel->g_task.icon_size1, panel->g_task.icon_size1);
256 imlib_free_image();
257
258 imlib_context_set_image(tsk->icon);
259 tsk->icon_width = imlib_image_get_width();
260 tsk->icon_height = imlib_image_get_height();
261 tsk->icon_active = imlib_clone_image();
262
263 DATA32 *data32;
264 if (panel->g_task.alpha != 100 || panel->g_task.saturation != 0 || panel->g_task.brightness != 0) {
265 data32 = imlib_image_get_data();
266 adjust_asb(data32, tsk->icon_width, tsk->icon_height, panel->g_task.alpha, (float)panel->g_task.saturation/100, (float)panel->g_task.brightness/100);
267 imlib_image_put_back_data(data32);
268 }
269
270 if (panel->g_task.alpha_active != 100 || panel->g_task.saturation_active != 0 || panel->g_task.brightness_active != 0) {
271 imlib_context_set_image(tsk->icon_active);
272 data32 = imlib_image_get_data();
273 adjust_asb(data32, tsk->icon_width, tsk->icon_height, panel->g_task.alpha_active, (float)panel->g_task.saturation_active/100, (float)panel->g_task.brightness_active/100);
274 imlib_image_put_back_data(data32);
275 }
276
277 if (hints)
278 XFree(hints);
279 if (data)
280 XFree (data);
281 }
282
283
284 void draw_task_icon (Task *tsk, int text_width, int active)
285 {
286 if (tsk->icon == NULL || tsk->icon_active == NULL) return;
287
288 // Find pos
289 int pos_x;
290 Panel *panel = (Panel*)tsk->area.panel;
291 if (panel->g_task.centered) {
292 if (panel->g_task.text)
293 pos_x = (tsk->area.width - text_width - panel->g_task.icon_size1) / 2;
294 else
295 pos_x = (tsk->area.width - panel->g_task.icon_size1) / 2;
296 }
297 else pos_x = panel->g_task.area.paddingxlr + panel->g_task.area.pix.border.width;
298
299 // Render
300 Pixmap *pmap;
301 if (active == 0) {
302 imlib_context_set_image (tsk->icon);
303 pmap = &tsk->area.pix.pmap;
304 }
305 else {
306 imlib_context_set_image (tsk->icon_active);
307 pmap = &tsk->area.pix_active.pmap;
308 }
309 imlib_context_set_drawable (*pmap);
310 imlib_render_image_on_drawable (pos_x, panel->g_task.icon_posy);
311 }
312
313
314 void draw_task (void *obj, cairo_t *c, int active)
315 {
316 Task *tsk = obj;
317 PangoLayout *layout;
318 config_color *config_text;
319 int width=0, height;
320 Panel *panel = (Panel*)tsk->area.panel;
321
322 if (panel->g_task.text) {
323 /* Layout */
324 layout = pango_cairo_create_layout (c);
325 pango_layout_set_font_description (layout, panel->g_task.font_desc);
326 pango_layout_set_text (layout, tsk->title, -1);
327
328 /* Drawing width and Cut text */
329 // pango use U+22EF or U+2026
330 pango_layout_set_width (layout, ((Taskbar*)tsk->area.parent)->text_width * PANGO_SCALE);
331 pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
332
333 /* Center text */
334 if (panel->g_task.centered) pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
335 else pango_layout_set_alignment (layout, PANGO_ALIGN_LEFT);
336
337 pango_layout_get_pixel_size (layout, &width, &height);
338
339 if (active) config_text = &panel->g_task.font_active;
340 else config_text = &panel->g_task.font;
341
342 cairo_set_source_rgba (c, config_text->color[0], config_text->color[1], config_text->color[2], config_text->alpha);
343
344 pango_cairo_update_layout (c, layout);
345 cairo_move_to (c, panel->g_task.text_posx, panel->g_task.text_posy);
346 pango_cairo_show_layout (c, layout);
347
348 if (panel->g_task.font_shadow) {
349 cairo_set_source_rgba (c, 0.0, 0.0, 0.0, 0.5);
350 pango_cairo_update_layout (c, layout);
351 cairo_move_to (c, panel->g_task.text_posx + 1, panel->g_task.text_posy + 1);
352 pango_cairo_show_layout (c, layout);
353 }
354 g_object_unref (layout);
355 }
356
357 if (panel->g_task.icon) {
358 // icon use same opacity as text
359 draw_task_icon (tsk, width, active);
360 }
361 }
362
363
364 void active_task()
365 {
366 GSList *l0;
367 int i, j;
368 Task *tsk1, *tsk2;
369
370 if (task_active) {
371 for (i=0 ; i < nb_panel ; i++) {
372 for (j=0 ; j < panel1[i].nb_desktop ; j++) {
373 for (l0 = panel1[i].taskbar[j].area.list; l0 ; l0 = l0->next) {
374 tsk1 = l0->data;
375 tsk1->area.is_active = 0;
376 }
377 }
378 }
379 task_active = 0;
380 }
381
382 Window w1 = window_get_active ();
383 //printf("Change active task %ld\n", w1);
384
385 tsk2 = task_get_task(w1);
386 if (!tsk2) {
387 Window w2;
388 if (XGetTransientForHint(server.dsp, w1, &w2) != 0)
389 if (w2) tsk2 = task_get_task(w2);
390 }
391 if ( is_urgent(tsk2) ) {
392 del_urgent(tsk2);
393 }
394 // put active state on all task (multi_desktop)
395 if (tsk2) {
396 for (i=0 ; i < nb_panel ; i++) {
397 for (j=0 ; j < panel1[i].nb_desktop ; j++) {
398 for (l0 = panel1[i].taskbar[j].area.list; l0 ; l0 = l0->next) {
399 tsk1 = l0->data;
400 if (tsk1->win == tsk2->win) {
401 tsk1->area.is_active = 1;
402 }
403 }
404 }
405 }
406 task_active = tsk2;
407 }
408 }
409
410
411 void blink_urgent()
412 {
413 GSList* urgent_task = urgent_list;
414 while (urgent_task) {
415 Task_urgent* t = urgent_task->data;
416 if ( t->tick < max_tick_urgent) {
417 t->tsk->area.is_active = !t->tsk->area.is_active;
418 t->tsk->area.redraw = 1;
419 t->tick++;
420 }
421 urgent_task = urgent_task->next;
422 }
423 panel_refresh = 1;
424 }
425
426
427 void add_urgent(Task *tsk)
428 {
429 // first check if task is already in the list and reset the counter
430 GSList* urgent_task = urgent_list;
431 while (urgent_task) {
432 Task_urgent* t = urgent_task->data;
433 if (t->tsk == tsk) {
434 t->tick = 0;
435 return;
436 }
437 urgent_task = urgent_task->next;
438 }
439
440 // not yet in the list, so we have to add it
441 Task_urgent* t = malloc(sizeof(Task_urgent));
442 if (!t)
443 return;
444 t->tsk = tsk;
445 t->tick = 0;
446 urgent_list = g_slist_prepend(urgent_list, t);
447
448 if (urgent_timer == 0)
449 urgent_timer = install_timer(0, 1000000, 1, 0, blink_urgent);
450 else
451 reset_timer(urgent_timer, 0, 1000000, 1, 0);
452 }
453
454
455 void del_urgent(Task *tsk)
456 {
457 GSList* urgent_task = urgent_list;
458 while (urgent_task) {
459 Task_urgent* t = urgent_task->data;
460 if (t->tsk == tsk) {
461 urgent_list = g_slist_remove(urgent_list, t);
462 free(t);
463 if (!urgent_list)
464 reset_timer(urgent_timer, 0, 0, 0, 0);
465 return;
466 }
467 urgent_task = urgent_task->next;
468 }
469 }
470
471 int is_urgent(Task *tsk)
472 {
473 GSList* urgent_task = urgent_list;
474 while (urgent_task) {
475 Task_urgent* t = urgent_task->data;
476 if (t->tsk == tsk)
477 return 1;
478 urgent_task = urgent_task->next;
479 }
480 return 0;
481 }
This page took 0.0507 seconds and 5 git commands to generate.