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 **************************************************************************/
22 #include <X11/Xutil.h>
23 #include <X11/Xatom.h>
25 #include <cairo-xlib.h>
26 #include <pango/pangocairo.h>
36 // --------------------------------------------------
41 int mouse_scroll_down
;
47 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
;
74 const gchar
* const *data_dirs
;
75 data_dirs
= g_get_system_data_dirs ();
76 for (i
= 0; data_dirs
[i
] != NULL
; i
++) {
77 path
= g_build_filename(data_dirs
[i
], "tint2", "default_icon.png", NULL
);
78 if (g_file_test (path
, G_FILE_TEST_EXISTS
))
79 default_icon
= imlib_load_image(path
);
85 // alloc panels (one monitor or all monitors)
86 if (panel_config
.monitor
>= 0)
89 nb_panel
= server
.nb_monitor
;
90 panel1
= malloc(nb_panel
* sizeof(Panel
));
92 for (i
=0 ; i
< nb_panel
; i
++) {
95 memcpy(p
, &panel_config
, sizeof(Panel
));
99 p
->area
.on_screen
= 1;
101 p
->area
._resize
= resize_panel
;
102 p
->g_taskbar
.parent
= p
;
103 p
->g_taskbar
.panel
= p
;
104 p
->g_task
.area
.panel
= p
;
107 if (p
->clock
.area
.on_screen
)
108 p
->area
.list
= g_slist_append(p
->area
.list
, &p
->clock
);
109 #ifdef ENABLE_BATTERY
110 if (p
->battery
.area
.on_screen
)
111 p
->area
.list
= g_slist_append(p
->area
.list
, &p
->battery
);
113 // systray only on first panel
114 if (systray
.area
.on_screen
&& i
== 0)
115 p
->area
.list
= g_slist_append(p
->area
.list
, &systray
);
118 if (!p
->initial_width
) {
119 p
->initial_width
= 100;
123 init_panel_size_and_position(p
);
126 long event_mask
= ExposureMask
|ButtonPressMask
|ButtonReleaseMask
;
127 if (g_tooltip
.enabled
)
128 event_mask
|= PointerMotionMask
|LeaveWindowMask
;
129 XSetWindowAttributes att
= { ParentRelative
, 0L, 0, 0L, 0, 0, Always
, 0L, 0L, False
, event_mask
, NoEventMask
, False
, 0, 0 };
130 if (p
->main_win
) XDestroyWindow(server
.dsp
, p
->main_win
);
131 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
);
133 set_panel_properties(p
);
134 set_panel_background(p
);
136 XMapWindow (server
.dsp
, p
->main_win
);
142 void init_panel_size_and_position(Panel
*panel
)
145 if (panel_horizontal
) {
146 if (panel
->pourcentx
)
147 panel
->area
.width
= (float)server
.monitor
[panel
->monitor
].width
* panel
->initial_width
/ 100;
149 panel
->area
.width
= panel
->initial_width
;
150 if (panel
->pourcenty
)
151 panel
->area
.height
= (float)server
.monitor
[panel
->monitor
].height
* panel
->initial_height
/ 100;
153 panel
->area
.height
= panel
->initial_height
;
154 if (panel
->area
.pix
.border
.rounded
> panel
->area
.height
/2)
155 panel
->area
.pix
.border
.rounded
= panel
->area
.height
/2;
158 if (panel
->pourcentx
)
159 panel
->area
.height
= (float)server
.monitor
[panel
->monitor
].height
* panel
->initial_width
/ 100;
161 panel
->area
.height
= panel
->initial_width
;
162 if (panel
->pourcenty
)
163 panel
->area
.width
= (float)server
.monitor
[panel
->monitor
].width
* panel
->initial_height
/ 100;
165 panel
->area
.width
= panel
->initial_height
;
166 if (panel
->area
.pix
.border
.rounded
> panel
->area
.width
/2)
167 panel
->area
.pix
.border
.rounded
= panel
->area
.width
/2;
170 // panel position determined here
171 if (panel_position
& LEFT
) {
172 panel
->posx
= server
.monitor
[panel
->monitor
].x
+ panel
->marginx
;
175 if (panel_position
& RIGHT
) {
176 panel
->posx
= server
.monitor
[panel
->monitor
].x
+ server
.monitor
[panel
->monitor
].width
- panel
->area
.width
- panel
->marginx
;
179 if (panel_horizontal
)
180 panel
->posx
= server
.monitor
[panel
->monitor
].x
+ ((server
.monitor
[panel
->monitor
].width
- panel
->area
.width
) / 2);
182 panel
->posx
= server
.monitor
[panel
->monitor
].x
+ panel
->marginx
;
185 if (panel_position
& TOP
) {
186 panel
->posy
= server
.monitor
[panel
->monitor
].y
+ panel
->marginy
;
189 if (panel_position
& BOTTOM
) {
190 panel
->posy
= server
.monitor
[panel
->monitor
].y
+ server
.monitor
[panel
->monitor
].height
- panel
->area
.height
- panel
->marginy
;
193 panel
->posy
= server
.monitor
[panel
->monitor
].y
+ ((server
.monitor
[panel
->monitor
].height
- panel
->area
.height
) / 2);
196 // printf("panel : posx %d, posy %d, width %d, height %d\n", panel->posx, panel->posy, panel->area.width, panel->area.height);
211 imlib_context_set_image(default_icon
);
215 // font allocated once
216 if (panel1
[0].g_task
.font_desc
) {
217 pango_font_description_free(panel1
[0].g_task
.font_desc
);
218 panel1
[0].g_task
.font_desc
= 0;
223 for (i
=0 ; i
< nb_panel
; i
++) {
227 free_area(&p
->g_task
.area
);
228 free_area(&p
->g_taskbar
);
231 XFreePixmap(server
.dsp
, p
->temp_pmap
);
235 XDestroyWindow(server
.dsp
, p
->main_win
);
240 if (panel1
) free(panel1
);
243 if (g_tooltip
.window
) {
244 XDestroyWindow(server
.dsp
, g_tooltip
.window
);
245 g_tooltip
.window
= 0;
247 if (g_tooltip
.font_desc
) {
248 pango_font_description_free(g_tooltip
.font_desc
);
249 g_tooltip
.font_desc
= 0;
254 void resize_panel(void *obj
)
256 Panel
*panel
= (Panel
*)obj
;
258 if (panel_horizontal
) {
259 int taskbar_width
, modulo_width
= 0;
261 taskbar_width
= panel
->area
.width
- (2 * panel
->area
.paddingxlr
) - (2 * panel
->area
.pix
.border
.width
);
262 if (panel
->clock
.area
.on_screen
&& panel
->clock
.area
.width
)
263 taskbar_width
-= (panel
->clock
.area
.width
+ panel
->area
.paddingx
);
264 #ifdef ENABLE_BATTERY
265 if (panel
->battery
.area
.on_screen
&& panel
->battery
.area
.width
)
266 taskbar_width
-= (panel
->battery
.area
.width
+ panel
->area
.paddingx
);
268 // TODO : systray only on first panel. search better implementation !
269 if (systray
.area
.on_screen
&& systray
.area
.width
&& panel
== &panel1
[0])
270 taskbar_width
-= (systray
.area
.width
+ panel
->area
.paddingx
);
272 if (panel_mode
== MULTI_DESKTOP
) {
273 int width
= taskbar_width
- ((panel
->nb_desktop
-1) * panel
->area
.paddingx
);
274 taskbar_width
= width
/ panel
->nb_desktop
;
275 modulo_width
= width
% panel
->nb_desktop
;
278 // change posx and width for all taskbar
280 posx
= panel
->area
.pix
.border
.width
+ panel
->area
.paddingxlr
;
281 for (i
=0 ; i
< panel
->nb_desktop
; i
++) {
282 panel
->taskbar
[i
].area
.posx
= posx
;
283 panel
->taskbar
[i
].area
.width
= taskbar_width
;
284 panel
->taskbar
[i
].area
.resize
= 1;
286 panel
->taskbar
[i
].area
.width
++;
289 //printf("taskbar %d : posx %d, width, %d, posy %d\n", i, posx, panel->taskbar[i].area.width, posx + panel->taskbar[i].area.width);
290 if (panel_mode
== MULTI_DESKTOP
)
291 posx
+= panel
->taskbar
[i
].area
.width
+ panel
->area
.paddingx
;
295 int taskbar_height
, modulo_height
= 0;
298 taskbar_height
= panel
->area
.height
- (2 * panel
->area
.paddingxlr
) - (2 * panel
->area
.pix
.border
.width
);
299 if (panel
->clock
.area
.on_screen
&& panel
->clock
.area
.height
)
300 taskbar_height
-= (panel
->clock
.area
.height
+ panel
->area
.paddingx
);
301 #ifdef ENABLE_BATTERY
302 if (panel
->battery
.area
.on_screen
&& panel
->battery
.area
.height
)
303 taskbar_height
-= (panel
->battery
.area
.height
+ panel
->area
.paddingx
);
305 // TODO : systray only on first panel. search better implementation !
306 if (systray
.area
.on_screen
&& systray
.area
.height
&& panel
== &panel1
[0])
307 taskbar_height
-= (systray
.area
.height
+ panel
->area
.paddingx
);
309 posy
= panel
->area
.height
- panel
->area
.pix
.border
.width
- panel
->area
.paddingxlr
- taskbar_height
;
310 if (panel_mode
== MULTI_DESKTOP
) {
311 int height
= taskbar_height
- ((panel
->nb_desktop
-1) * panel
->area
.paddingx
);
312 taskbar_height
= height
/ panel
->nb_desktop
;
313 modulo_height
= height
% panel
->nb_desktop
;
316 // change posy and height for all taskbar
317 for (i
=0 ; i
< panel
->nb_desktop
; i
++) {
318 panel
->taskbar
[i
].area
.posy
= posy
;
319 panel
->taskbar
[i
].area
.height
= taskbar_height
;
320 panel
->taskbar
[i
].area
.resize
= 1;
322 panel
->taskbar
[i
].area
.height
++;
325 if (panel_mode
== MULTI_DESKTOP
)
326 posy
+= panel
->taskbar
[i
].area
.height
+ panel
->area
.paddingx
;
332 void visible_object()
337 for (i
=0 ; i
< nb_panel
; i
++) {
341 for (j
=0 ; j
< panel
->nb_desktop
; j
++) {
342 taskbar
= &panel
->taskbar
[j
];
343 if (panel_mode
!= MULTI_DESKTOP
&& taskbar
->desktop
!= server
.desktop
) {
344 // SINGLE_DESKTOP and not current desktop
345 taskbar
->area
.on_screen
= 0;
348 taskbar
->area
.on_screen
= 1;
356 void set_panel_properties(Panel
*p
)
358 XStoreName (server
.dsp
, p
->main_win
, "tint2");
361 gchar
*name
= g_locale_to_utf8("tint2", -1, NULL
, &len
, NULL
);
363 XChangeProperty(server
.dsp
, p
->main_win
, server
.atom
._NET_WM_NAME
, server
.atom
.UTF8_STRING
, 8, PropModeReplace
, (unsigned char *) name
, (int) len
);
368 long val
= server
.atom
._NET_WM_WINDOW_TYPE_DOCK
;
369 XChangeProperty (server
.dsp
, p
->main_win
, server
.atom
._NET_WM_WINDOW_TYPE
, XA_ATOM
, 32, PropModeReplace
, (unsigned char *) &val
, 1);
372 long struts
[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
373 if (panel_horizontal
) {
374 if (panel_position
& TOP
) {
375 struts
[2] = p
->area
.height
+ p
->marginy
;
377 // p->area.width - 1 allowed full screen on monitor 2
378 struts
[9] = p
->posx
+ p
->area
.width
- 1;
381 struts
[3] = p
->area
.height
+ p
->marginy
;
382 struts
[10] = p
->posx
;
383 // p->area.width - 1 allowed full screen on monitor 2
384 struts
[11] = p
->posx
+ p
->area
.width
- 1;
388 if (panel_position
& LEFT
) {
389 struts
[0] = p
->area
.width
+ p
->marginx
;
391 // p->area.width - 1 allowed full screen on monitor 2
392 struts
[5] = p
->posy
+ p
->area
.height
- 1;
395 struts
[1] = p
->area
.width
+ p
->marginx
;
397 // p->area.width - 1 allowed full screen on monitor 2
398 struts
[7] = p
->posy
+ p
->area
.height
- 1;
401 // Old specification : fluxbox need _NET_WM_STRUT.
402 XChangeProperty (server
.dsp
, p
->main_win
, server
.atom
._NET_WM_STRUT
, XA_CARDINAL
, 32, PropModeReplace
, (unsigned char *) &struts
, 4);
403 XChangeProperty (server
.dsp
, p
->main_win
, server
.atom
._NET_WM_STRUT_PARTIAL
, XA_CARDINAL
, 32, PropModeReplace
, (unsigned char *) &struts
, 12);
405 // Sticky and below other window
407 XChangeProperty (server
.dsp
, p
->main_win
, server
.atom
._NET_WM_DESKTOP
, XA_CARDINAL
, 32, PropModeReplace
, (unsigned char *) &val
, 1);
409 state
[0] = server
.atom
._NET_WM_STATE_SKIP_PAGER
;
410 state
[1] = server
.atom
._NET_WM_STATE_SKIP_TASKBAR
;
411 state
[2] = server
.atom
._NET_WM_STATE_STICKY
;
412 state
[3] = server
.atom
._NET_WM_STATE_BELOW
;
413 XChangeProperty (server
.dsp
, p
->main_win
, server
.atom
._NET_WM_STATE
, XA_ATOM
, 32, PropModeReplace
, (unsigned char *) state
, 4);
415 // Fixed position and non-resizable window
416 XSizeHints size_hints
;
417 size_hints
.flags
= PPosition
|PMinSize
|PMaxSize
;
418 size_hints
.min_width
= size_hints
.max_width
= p
->area
.width
;
419 size_hints
.min_height
= size_hints
.max_height
= p
->area
.height
;
420 XSetWMNormalHints(server
.dsp
, p
->main_win
, &size_hints
);
425 // TODO: Xdnd feature cannot be used in withdrawn state at the moment (at least GTK apps fail, qt seems to work)
426 wmhints
.icon_window
= wmhints
.window_group
= p
->main_win
;
427 wmhints
.flags
= StateHint
| IconWindowHint
;
428 wmhints
.initial_state
= WithdrawnState
;
431 wmhints
.flags
= InputHint
;
432 wmhints
.input
= False
;
434 XSetWMHints(server
.dsp
, p
->main_win
, &wmhints
);
437 long prop
[5] = { 2, 0, 0, 0, 0 };
438 XChangeProperty(server
.dsp
, p
->main_win
, server
.atom
._MOTIF_WM_HINTS
, server
.atom
._MOTIF_WM_HINTS
, 32, PropModeReplace
, (unsigned char *) prop
, 5);
440 // XdndAware - Register for Xdnd events
442 XChangeProperty(server
.dsp
, p
->main_win
, server
.atom
.XdndAware
, XA_ATOM
, 32, PropModeReplace
, (unsigned char*)&version
, 1);
446 void set_panel_background(Panel
*p
)
450 if (p
->area
.pix
.pmap
) XFreePixmap (server
.dsp
, p
->area
.pix
.pmap
);
451 p
->area
.pix
.pmap
= XCreatePixmap (server
.dsp
, server
.root_win
, p
->area
.width
, p
->area
.height
, server
.depth
);
453 // copy background (server.root_pmap) in panel.area.pix.pmap
456 XTranslateCoordinates(server
.dsp
, p
->main_win
, server
.root_win
, 0, 0, &x
, &y
, &dummy
);
457 XSetTSOrigin(server
.dsp
, server
.gc
, -x
, -y
) ;
458 XFillRectangle(server
.dsp
, p
->area
.pix
.pmap
, server
.gc
, 0, 0, p
->area
.width
, p
->area
.height
);
460 // draw background panel
463 cs
= cairo_xlib_surface_create (server
.dsp
, p
->area
.pix
.pmap
, server
.visual
, p
->area
.width
, p
->area
.height
);
464 c
= cairo_create (cs
);
466 draw_background(&p
->area
, c
, 0);
469 cairo_surface_destroy (cs
);
471 // redraw panel's object
474 for (l0
= p
->area
.list
; l0
; l0
= l0
->next
) {
481 Panel
*get_panel(Window win
)
484 for (i
=0 ; i
< nb_panel
; i
++) {
485 if (panel1
[i
].main_win
== win
) {