1 /**************************************************************************
3 * Copyright (C) 2008 Pål Staurland (staura@gmail.com)
4 * Modified (C) 2008 thierry lorthiois (lorthiois@bbsoft.fr)
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License version 2
8 * as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 **************************************************************************/
23 #include <X11/Xutil.h>
24 #include <X11/Xatom.h>
26 #include <cairo-xlib.h>
27 #include <pango/pangocairo.h>
37 // --------------------------------------------------
42 int mouse_scroll_down
;
48 int panel_dock
=0; // default not in the dock
58 // panel's initial config
60 // panels (one panel per monitor)
64 Imlib_Image default_icon
= NULL
;
81 for (i
=0 ; i
< nb_panel
; i
++) {
82 free_area(&panel1
[i
].area
);
83 if (panel1
[i
].temp_pmap
) {
84 XFreePixmap(server
.dsp
, panel1
[i
].temp_pmap
);
85 panel1
[i
].temp_pmap
= 0;
90 old_nb_panel
= nb_panel
;
91 if (panel_config
.monitor
>= 0)
94 nb_panel
= server
.nb_monitor
;
97 for (i
=nb_panel
; i
< old_nb_panel
; i
++) {
98 if (panel1
[i
].main_win
) {
99 XDestroyWindow(server
.dsp
, panel1
[i
].main_win
);
100 panel1
[i
].main_win
= 0;
104 // alloc & init new panel
106 if (nb_panel
!= old_nb_panel
)
107 new_panel
= realloc(panel1
, nb_panel
* sizeof(Panel
));
110 for (i
=0 ; i
< nb_panel
; i
++) {
111 old_win
= new_panel
[i
].main_win
;
112 memcpy(&new_panel
[i
], &panel_config
, sizeof(Panel
));
113 new_panel
[i
].main_win
= old_win
;
116 fprintf(stderr
, "tint2 : nb monitor %d, nb monitor used %d, nb desktop %d\n", server
.nb_monitor
, nb_panel
, server
.nb_desktop
);
117 for (i
=0 ; i
< nb_panel
; i
++) {
120 if (panel_config
.monitor
< 0)
124 p
->area
.on_screen
= 1;
126 p
->area
._resize
= resize_panel
;
127 p
->g_taskbar
.parent
= p
;
128 p
->g_taskbar
.panel
= p
;
129 p
->g_task
.area
.panel
= p
;
130 init_panel_size_and_position(p
);
135 p
->area
.list
= g_slist_append(p
->area
.list
, &p
->clock
);
137 #ifdef ENABLE_BATTERY
138 if (battery_enabled
) {
139 init_battery_panel(p
);
140 p
->area
.list
= g_slist_append(p
->area
.list
, &p
->battery
);
143 // systray only on first panel
144 if (systray
.area
.on_screen
&& i
== 0) {
145 init_systray_panel(p
);
146 p
->area
.list
= g_slist_append(p
->area
.list
, &systray
);
150 if (i
>= old_nb_panel
) {
151 // new panel : catch some events
152 long event_mask
= ExposureMask
|ButtonPressMask
|ButtonReleaseMask
;
153 if (g_tooltip
.enabled
)
154 event_mask
|= PointerMotionMask
|LeaveWindowMask
;
155 XSetWindowAttributes att
= { ParentRelative
, 0L, 0, 0L, 0, 0, Always
, 0L, 0L, False
, event_mask
, NoEventMask
, False
, 0, 0 };
156 p
->main_win
= XCreateWindow(server
.dsp
, server
.root_win
, p
->posx
, p
->posy
, p
->area
.width
, p
->area
.height
, 0, server
.depth
, InputOutput
, CopyFromParent
, CWEventMask
, &att
);
160 XMoveResizeWindow(server
.dsp
, p
->main_win
, p
->posx
, p
->posy
, p
->area
.width
, p
->area
.height
);
163 //printf("panel %d : %d, %d, %d, %d\n", i, p->posx, p->posy, p->area.width, p->area.height);
164 set_panel_properties(p
);
165 set_panel_background(p
);
166 if (i
>= old_nb_panel
) {
168 XMapWindow (server
.dsp
, p
->main_win
);
176 task_refresh_tasklist();
181 void init_panel_size_and_position(Panel
*panel
)
184 if (panel_horizontal
) {
185 if (panel
->pourcentx
)
186 panel
->area
.width
= (float)server
.monitor
[panel
->monitor
].width
* panel
->area
.width
/ 100;
187 if (panel
->pourcenty
)
188 panel
->area
.height
= (float)server
.monitor
[panel
->monitor
].height
* panel
->area
.height
/ 100;
189 if (panel
->area
.pix
.border
.rounded
> panel
->area
.height
/2)
190 panel
->area
.pix
.border
.rounded
= panel
->area
.height
/2;
193 int old_panel_height
= panel
->area
.height
;
194 if (panel
->pourcentx
)
195 panel
->area
.height
= (float)server
.monitor
[panel
->monitor
].height
* panel
->area
.width
/ 100;
197 panel
->area
.height
= panel
->area
.width
;
198 if (panel
->pourcenty
)
199 panel
->area
.width
= (float)server
.monitor
[panel
->monitor
].width
* old_panel_height
/ 100;
201 panel
->area
.width
= old_panel_height
;
202 if (panel
->area
.pix
.border
.rounded
> panel
->area
.width
/2)
203 panel
->area
.pix
.border
.rounded
= panel
->area
.width
/2;
206 // panel position determined here
207 if (panel_position
& LEFT
) {
208 panel
->posx
= server
.monitor
[panel
->monitor
].x
+ panel
->marginx
;
211 if (panel_position
& RIGHT
) {
212 panel
->posx
= server
.monitor
[panel
->monitor
].x
+ server
.monitor
[panel
->monitor
].width
- panel
->area
.width
- panel
->marginx
;
215 if (panel_horizontal
)
216 panel
->posx
= server
.monitor
[panel
->monitor
].x
+ ((server
.monitor
[panel
->monitor
].width
- panel
->area
.width
) / 2);
218 panel
->posx
= server
.monitor
[panel
->monitor
].x
+ panel
->marginx
;
221 if (panel_position
& TOP
) {
222 panel
->posy
= server
.monitor
[panel
->monitor
].y
+ panel
->marginy
;
225 if (panel_position
& BOTTOM
) {
226 panel
->posy
= server
.monitor
[panel
->monitor
].y
+ server
.monitor
[panel
->monitor
].height
- panel
->area
.height
- panel
->marginy
;
229 panel
->posy
= server
.monitor
[panel
->monitor
].y
+ ((server
.monitor
[panel
->monitor
].height
- panel
->area
.height
) / 2);
232 // printf("panel : posx %d, posy %d, width %d, height %d\n", panel->posx, panel->posy, panel->area.width, panel->area.height);
242 while (urgent_list
) {
243 Task_urgent
* t
= urgent_list
->data
;
244 urgent_list
= g_slist_remove(urgent_list
, urgent_list
->data
);
252 for (i
=0 ; i
< nb_panel
; i
++) {
258 XFreePixmap(server
.dsp
, p
->temp_pmap
);
262 XDestroyWindow(server
.dsp
, p
->main_win
);
273 if (panel_config
.g_task
.font_desc
) {
274 pango_font_description_free(panel_config
.g_task
.font_desc
);
275 panel_config
.g_task
.font_desc
= 0;
280 void resize_panel(void *obj
)
282 Panel
*panel
= (Panel
*)obj
;
284 if (panel_horizontal
) {
285 int taskbar_width
, modulo_width
= 0;
287 taskbar_width
= panel
->area
.width
- (2 * panel
->area
.paddingxlr
) - (2 * panel
->area
.pix
.border
.width
);
288 if (panel
->clock
.area
.on_screen
&& panel
->clock
.area
.width
)
289 taskbar_width
-= (panel
->clock
.area
.width
+ panel
->area
.paddingx
);
290 #ifdef ENABLE_BATTERY
291 if (panel
->battery
.area
.on_screen
&& panel
->battery
.area
.width
)
292 taskbar_width
-= (panel
->battery
.area
.width
+ panel
->area
.paddingx
);
294 // TODO : systray only on first panel. search better implementation !
295 if (systray
.area
.on_screen
&& systray
.area
.width
&& panel
== &panel1
[0])
296 taskbar_width
-= (systray
.area
.width
+ panel
->area
.paddingx
);
298 if (panel_mode
== MULTI_DESKTOP
) {
299 int width
= taskbar_width
- ((panel
->nb_desktop
-1) * panel
->area
.paddingx
);
300 taskbar_width
= width
/ panel
->nb_desktop
;
301 modulo_width
= width
% panel
->nb_desktop
;
304 // change posx and width for all taskbar
306 posx
= panel
->area
.pix
.border
.width
+ panel
->area
.paddingxlr
;
307 for (i
=0 ; i
< panel
->nb_desktop
; i
++) {
308 panel
->taskbar
[i
].area
.posx
= posx
;
309 panel
->taskbar
[i
].area
.width
= taskbar_width
;
310 panel
->taskbar
[i
].area
.resize
= 1;
312 panel
->taskbar
[i
].area
.width
++;
315 //printf("taskbar %d : posx %d, width, %d, posy %d\n", i, posx, panel->taskbar[i].area.width, posx + panel->taskbar[i].area.width);
316 if (panel_mode
== MULTI_DESKTOP
)
317 posx
+= panel
->taskbar
[i
].area
.width
+ panel
->area
.paddingx
;
321 int taskbar_height
, modulo_height
= 0;
324 taskbar_height
= panel
->area
.height
- (2 * panel
->area
.paddingxlr
) - (2 * panel
->area
.pix
.border
.width
);
325 if (panel
->clock
.area
.on_screen
&& panel
->clock
.area
.height
)
326 taskbar_height
-= (panel
->clock
.area
.height
+ panel
->area
.paddingx
);
327 #ifdef ENABLE_BATTERY
328 if (panel
->battery
.area
.on_screen
&& panel
->battery
.area
.height
)
329 taskbar_height
-= (panel
->battery
.area
.height
+ panel
->area
.paddingx
);
331 // TODO : systray only on first panel. search better implementation !
332 if (systray
.area
.on_screen
&& systray
.area
.height
&& panel
== &panel1
[0])
333 taskbar_height
-= (systray
.area
.height
+ panel
->area
.paddingx
);
335 posy
= panel
->area
.height
- panel
->area
.pix
.border
.width
- panel
->area
.paddingxlr
- taskbar_height
;
336 if (panel_mode
== MULTI_DESKTOP
) {
337 int height
= taskbar_height
- ((panel
->nb_desktop
-1) * panel
->area
.paddingx
);
338 taskbar_height
= height
/ panel
->nb_desktop
;
339 modulo_height
= height
% panel
->nb_desktop
;
342 // change posy and height for all taskbar
343 for (i
=0 ; i
< panel
->nb_desktop
; i
++) {
344 panel
->taskbar
[i
].area
.posy
= posy
;
345 panel
->taskbar
[i
].area
.height
= taskbar_height
;
346 panel
->taskbar
[i
].area
.resize
= 1;
348 panel
->taskbar
[i
].area
.height
++;
351 if (panel_mode
== MULTI_DESKTOP
)
352 posy
+= panel
->taskbar
[i
].area
.height
+ panel
->area
.paddingx
;
358 void visible_object()
363 for (i
=0 ; i
< nb_panel
; i
++) {
367 for (j
=0 ; j
< panel
->nb_desktop
; j
++) {
368 taskbar
= &panel
->taskbar
[j
];
369 if (panel_mode
!= MULTI_DESKTOP
&& taskbar
->desktop
!= server
.desktop
) {
370 // SINGLE_DESKTOP and not current desktop
371 taskbar
->area
.on_screen
= 0;
374 taskbar
->area
.on_screen
= 1;
382 void set_panel_properties(Panel
*p
)
384 XStoreName (server
.dsp
, p
->main_win
, "tint2");
387 gchar
*name
= g_locale_to_utf8("tint2", -1, NULL
, &len
, NULL
);
389 XChangeProperty(server
.dsp
, p
->main_win
, server
.atom
._NET_WM_NAME
, server
.atom
.UTF8_STRING
, 8, PropModeReplace
, (unsigned char *) name
, (int) len
);
394 long val
= server
.atom
._NET_WM_WINDOW_TYPE_DOCK
;
395 XChangeProperty (server
.dsp
, p
->main_win
, server
.atom
._NET_WM_WINDOW_TYPE
, XA_ATOM
, 32, PropModeReplace
, (unsigned char *) &val
, 1);
397 // Sticky and below other window
399 XChangeProperty (server
.dsp
, p
->main_win
, server
.atom
._NET_WM_DESKTOP
, XA_CARDINAL
, 32, PropModeReplace
, (unsigned char *) &val
, 1);
401 state
[0] = server
.atom
._NET_WM_STATE_SKIP_PAGER
;
402 state
[1] = server
.atom
._NET_WM_STATE_SKIP_TASKBAR
;
403 state
[2] = server
.atom
._NET_WM_STATE_STICKY
;
404 state
[3] = server
.atom
._NET_WM_STATE_BELOW
;
405 XChangeProperty (server
.dsp
, p
->main_win
, server
.atom
._NET_WM_STATE
, XA_ATOM
, 32, PropModeReplace
, (unsigned char *) state
, 4);
410 // TODO: Xdnd feature cannot be used in withdrawn state at the moment (at least GTK apps fail, qt seems to work)
411 wmhints
.icon_window
= wmhints
.window_group
= p
->main_win
;
412 wmhints
.flags
= StateHint
| IconWindowHint
;
413 wmhints
.initial_state
= WithdrawnState
;
416 wmhints
.flags
= InputHint
;
417 wmhints
.input
= False
;
419 XSetWMHints(server
.dsp
, p
->main_win
, &wmhints
);
422 long prop
[5] = { 2, 0, 0, 0, 0 };
423 XChangeProperty(server
.dsp
, p
->main_win
, server
.atom
._MOTIF_WM_HINTS
, server
.atom
._MOTIF_WM_HINTS
, 32, PropModeReplace
, (unsigned char *) prop
, 5);
425 // XdndAware - Register for Xdnd events
427 XChangeProperty(server
.dsp
, p
->main_win
, server
.atom
.XdndAware
, XA_ATOM
, 32, PropModeReplace
, (unsigned char*)&version
, 1);
430 long struts
[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
431 if (panel_horizontal
) {
432 if (panel_position
& TOP
) {
433 struts
[2] = p
->area
.height
+ p
->marginy
;
435 // p->area.width - 1 allowed full screen on monitor 2
436 struts
[9] = p
->posx
+ p
->area
.width
- 1;
439 struts
[3] = p
->area
.height
+ p
->marginy
;
440 struts
[10] = p
->posx
;
441 // p->area.width - 1 allowed full screen on monitor 2
442 struts
[11] = p
->posx
+ p
->area
.width
- 1;
446 if (panel_position
& LEFT
) {
447 struts
[0] = p
->area
.width
+ p
->marginx
;
449 // p->area.width - 1 allowed full screen on monitor 2
450 struts
[5] = p
->posy
+ p
->area
.height
- 1;
453 struts
[1] = p
->area
.width
+ p
->marginx
;
455 // p->area.width - 1 allowed full screen on monitor 2
456 struts
[7] = p
->posy
+ p
->area
.height
- 1;
459 // Old specification : fluxbox need _NET_WM_STRUT.
460 XChangeProperty (server
.dsp
, p
->main_win
, server
.atom
._NET_WM_STRUT
, XA_CARDINAL
, 32, PropModeReplace
, (unsigned char *) &struts
, 4);
461 XChangeProperty (server
.dsp
, p
->main_win
, server
.atom
._NET_WM_STRUT_PARTIAL
, XA_CARDINAL
, 32, PropModeReplace
, (unsigned char *) &struts
, 12);
463 // Fixed position and non-resizable window
464 // Allow panel move and resize when tint2 reload config file
465 XSizeHints size_hints
;
466 size_hints
.flags
= PPosition
|PMinSize
|PMaxSize
;
467 size_hints
.min_width
= size_hints
.max_width
= p
->area
.width
;
468 size_hints
.min_height
= size_hints
.max_height
= p
->area
.height
;
469 XSetWMNormalHints(server
.dsp
, p
->main_win
, &size_hints
);
473 void set_panel_background(Panel
*p
)
477 if (p
->area
.pix
.pmap
) XFreePixmap (server
.dsp
, p
->area
.pix
.pmap
);
478 p
->area
.pix
.pmap
= XCreatePixmap (server
.dsp
, server
.root_win
, p
->area
.width
, p
->area
.height
, server
.depth
);
480 // copy background (server.root_pmap) in panel.area.pix.pmap
483 XTranslateCoordinates(server
.dsp
, p
->main_win
, server
.root_win
, 0, 0, &x
, &y
, &dummy
);
484 XSetTSOrigin(server
.dsp
, server
.gc
, -x
, -y
) ;
485 XFillRectangle(server
.dsp
, p
->area
.pix
.pmap
, server
.gc
, 0, 0, p
->area
.width
, p
->area
.height
);
487 // draw background panel
490 cs
= cairo_xlib_surface_create (server
.dsp
, p
->area
.pix
.pmap
, server
.visual
, p
->area
.width
, p
->area
.height
);
491 c
= cairo_create (cs
);
493 draw_background(&p
->area
, c
, 0);
496 cairo_surface_destroy (cs
);
498 // redraw panel's object
501 for (l0
= p
->area
.list
; l0
; l0
= l0
->next
) {
508 Panel
*get_panel(Window win
)
511 for (i
=0 ; i
< nb_panel
; i
++) {
512 if (panel1
[i
].main_win
== win
) {
520 Taskbar
*click_taskbar (Panel
*panel
, int x
, int y
)
525 if (panel_horizontal
) {
526 for (i
=0; i
< panel
->nb_desktop
; i
++) {
527 tskbar
= &panel
->taskbar
[i
];
528 if (tskbar
->area
.on_screen
&& x
>= tskbar
->area
.posx
&& x
<= (tskbar
->area
.posx
+ tskbar
->area
.width
))
533 for (i
=0; i
< panel
->nb_desktop
; i
++) {
534 tskbar
= &panel
->taskbar
[i
];
535 if (tskbar
->area
.on_screen
&& y
>= tskbar
->area
.posy
&& y
<= (tskbar
->area
.posy
+ tskbar
->area
.height
))
543 Task
*click_task (Panel
*panel
, int x
, int y
)
548 if ( (tskbar
= click_taskbar(panel
, x
, y
)) ) {
549 if (panel_horizontal
) {
551 for (l0
= tskbar
->area
.list
; l0
; l0
= l0
->next
) {
553 if (tsk
->area
.on_screen
&& x
>= tsk
->area
.posx
&& x
<= (tsk
->area
.posx
+ tsk
->area
.width
)) {
560 for (l0
= tskbar
->area
.list
; l0
; l0
= l0
->next
) {
562 if (tsk
->area
.on_screen
&& y
>= tsk
->area
.posy
&& y
<= (tsk
->area
.posy
+ tsk
->area
.height
)) {
572 int click_padding(Panel
*panel
, int x
, int y
)
574 if (panel_horizontal
) {
575 if (x
< panel
->area
.paddingxlr
|| x
> panel
->area
.width
-panel
->area
.paddingxlr
)
579 if (y
< panel
->area
.paddingxlr
|| y
> panel
->area
.height
-panel
->area
.paddingxlr
)
586 int click_clock(Panel
*panel
, int x
, int y
)
588 Clock clk
= panel
->clock
;
589 if (panel_horizontal
) {
590 if (clk
.area
.on_screen
&& x
>= clk
.area
.posx
&& x
<= (clk
.area
.posx
+ clk
.area
.width
))
593 if (clk
.area
.on_screen
&& y
>= clk
.area
.posy
&& y
<= (clk
.area
.posy
+ clk
.area
.height
))
600 Area
* click_area(Panel
*panel
, int x
, int y
)
602 Area
* result
= &panel
->area
;
603 Area
* new_result
= result
;
606 GSList
* it
= result
->list
;
609 if (panel_horizontal
) {
610 if (a
->on_screen
&& x
>= a
->posx
&& x
<= (a
->posx
+ a
->width
)) {
615 if (a
->on_screen
&& y
>= a
->posy
&& y
<= (a
->posy
+ a
->height
)) {
622 } while (new_result
!= result
);