4 #include <cairo-xlib.h>
10 // TODO: Use timer_create instead of setitimer, because SIGALRM is not the right signal for this...
11 // Reason: If we want to implement autohide we have to use another signal...
13 static int x
, y
, width
, height
;
15 // give the tooltip some reasonable default values
19 .show_timeout
= { .it_interval
={0, 0}, .it_value
={0, 0} },
20 .hide_timeout
= { .it_interval
={0, 0}, .it_value
={0, 0} },
22 .current_state
= TOOLTIP_ABOUT_TO_HIDE
,
26 .font_color
= { .color
={1, 1, 1}, .alpha
=1 },
27 .background_color
= { .color
={0.5, 0.4, 0.5}, .alpha
=1 },
28 .border
= { .color
={0, 0, 0}, .alpha
=1, .width
=1, .rounded
=0 },
34 if (!g_tooltip
.font_desc
)
35 g_tooltip
.font_desc
= pango_font_description_from_string("sans 10");
37 XSetWindowAttributes attr
;
38 attr
.override_redirect
= True
;
39 attr
.event_mask
= ExposureMask
;
40 if (g_tooltip
.window
) XDestroyWindow(server
.dsp
, g_tooltip
.window
);
41 g_tooltip
.window
= XCreateWindow(server
.dsp
, server
.root_win
, 0, 0, 100, 20, 0, server
.depth
, InputOutput
, CopyFromParent
, CWOverrideRedirect
|CWEventMask
, &attr
);
45 void tooltip_sighandler(int sig
)
47 if (g_tooltip
.current_state
== TOOLTIP_ABOUT_TO_SHOW
)
49 else if (g_tooltip
.current_state
== TOOLTIP_ABOUT_TO_HIDE
)
54 void tooltip_trigger_show(Task
* task
, int x_root
, int y_root
)
59 if (g_tooltip
.mapped
&& g_tooltip
.task
!= task
) {
60 g_tooltip
.task
= task
;
64 else if (!g_tooltip
.mapped
) {
65 g_tooltip
.current_state
= TOOLTIP_ABOUT_TO_SHOW
;
66 g_tooltip
.task
= task
;
67 struct timeval t
= g_tooltip
.show_timeout
.it_value
;
68 if (t
.tv_sec
== 0 && t
.tv_usec
== 0)
71 setitimer(ITIMER_REAL
, &g_tooltip
.show_timeout
, 0);
78 if (!g_tooltip
.mapped
) {
79 g_tooltip
.mapped
= True
;
80 XMapWindow(server
.dsp
, g_tooltip
.window
);
87 void tooltip_update_geometry()
92 cs
= cairo_xlib_surface_create(server
.dsp
, g_tooltip
.window
, server
.visual
, width
, height
);
94 layout
= pango_cairo_create_layout(c
);
95 pango_layout_set_font_description(layout
, g_tooltip
.font_desc
);
96 pango_layout_set_text(layout
, g_tooltip
.task
->title
, -1);
97 PangoRectangle r1
, r2
;
98 pango_layout_get_pixel_extents(layout
, &r1
, &r2
);
99 width
= 2*g_tooltip
.border
.width
+ 2*g_tooltip
.paddingx
+ r2
.width
;
100 height
= 2*g_tooltip
.border
.width
+ 2*g_tooltip
.paddingy
+ r2
.height
;
102 Panel
* panel
= g_tooltip
.task
->area
.panel
;
103 if (panel_horizontal
&& panel_position
& BOTTOM
)
104 y
= panel
->posy
-height
;
105 else if (panel_horizontal
&& panel_position
& TOP
)
106 y
= panel
->posy
+ panel
->area
.height
;
107 else if (panel_position
& LEFT
)
108 x
= panel
->posx
+ panel
->area
.width
;
110 x
= panel
->posx
- width
;
111 g_object_unref(layout
);
113 cairo_surface_destroy(cs
);
117 void tooltip_adjust_geometry()
119 // adjust coordinates and size to not go offscreen
120 // it seems quite impossible that the height needs to be adjusted, but we do it anyway.
122 int min_x
, min_y
, max_width
, max_height
;
123 Panel
* panel
= g_tooltip
.task
->area
.panel
;
124 int screen_width
= server
.monitor
[panel
->monitor
].width
;
125 int screen_height
= server
.monitor
[panel
->monitor
].height
;
126 if ( x
+width
<= screen_width
&& y
+height
<= screen_height
&& x
>=0 && y
>=0)
127 return; // no adjustment needed
129 if (panel_horizontal
) {
131 max_width
=screen_width
;
132 max_height
=screen_height
-panel
->area
.height
;
133 if (panel_position
& BOTTOM
)
136 min_y
=panel
->area
.height
;
139 max_width
=screen_width
-panel
->area
.width
;
141 max_height
=screen_height
;
142 if (panel_position
& LEFT
)
143 min_x
=panel
->area
.width
;
148 if (x
+width
> server
.monitor
[panel
->monitor
].width
)
149 x
= server
.monitor
[panel
->monitor
].width
-width
;
150 if ( y
+height
>server
.monitor
[panel
->monitor
].height
)
151 y
= server
.monitor
[panel
->monitor
].height
-height
;
159 if (height
>max_height
)
163 void tooltip_update()
165 if (!g_tooltip
.task
) {
170 tooltip_update_geometry();
171 tooltip_adjust_geometry();
172 XMoveResizeWindow(server
.dsp
, g_tooltip
.window
, x
, y
, width
, height
);
174 // Stuff for drawing the tooltip
178 cs
= cairo_xlib_surface_create(server
.dsp
, g_tooltip
.window
, server
.visual
, width
, height
);
179 c
= cairo_create(cs
);
180 Color bc
= g_tooltip
.background_color
;
181 cairo_rectangle(c
, 0, 0, width
, height
);
182 cairo_set_source_rgb(c
, bc
.color
[0], bc
.color
[1], bc
.color
[2]);
184 Border b
= g_tooltip
.border
;
185 cairo_set_source_rgba(c
, b
.color
[0], b
.color
[1], b
.color
[2], b
.alpha
);
186 cairo_set_line_width(c
, b
.width
);
187 cairo_rectangle(c
, b
.width
/2.0, b
.width
/2.0, width
-b
.width
, height
-b
.width
);
190 config_color fc
= g_tooltip
.font_color
;
191 cairo_set_source_rgba(c
, fc
.color
[0], fc
.color
[1], fc
.color
[2], fc
.alpha
);
192 layout
= pango_cairo_create_layout(c
);
193 pango_layout_set_font_description(layout
, g_tooltip
.font_desc
);
194 pango_layout_set_text(layout
, g_tooltip
.task
->title
, -1);
195 PangoRectangle r1
, r2
;
196 pango_layout_get_pixel_extents(layout
, &r1
, &r2
);
197 pango_layout_set_width(layout
, width
*PANGO_SCALE
);
198 pango_layout_set_height(layout
, height
*PANGO_SCALE
);
199 pango_layout_set_ellipsize(layout
, PANGO_ELLIPSIZE_END
);
200 // I do not know why this is the right way, but with the below cairo_move_to it seems to be centered (horiz. and vert.)
201 cairo_move_to(c
, -r1
.x
/2+g_tooltip
.border
.width
+g_tooltip
.paddingx
, -r1
.y
/2+g_tooltip
.border
.width
+g_tooltip
.paddingy
);
202 pango_cairo_show_layout (c
, layout
);
204 g_object_unref (layout
);
206 cairo_surface_destroy (cs
);
210 void tooltip_trigger_hide(Tooltip
* tooltip
)
212 if (g_tooltip
.mapped
) {
213 g_tooltip
.current_state
= TOOLTIP_ABOUT_TO_HIDE
;
214 struct timeval t
= g_tooltip
.hide_timeout
.it_value
;
215 if (t
.tv_sec
== 0 && t
.tv_usec
== 0)
218 setitimer(ITIMER_REAL
, &g_tooltip
.hide_timeout
, 0);
221 // tooltip not visible yet, but maybe an alarm is still pending
229 if (g_tooltip
.mapped
) {
230 g_tooltip
.mapped
= False
;
231 XUnmapWindow(server
.dsp
, g_tooltip
.window
);