1 /*
2 *
3 * Author: Giacomo Lozito <james@develia.org>, (C) 2005-2007
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
18 *
19 */
20 
21 #include "aosd_style.h"
22 #include "aosd_style_private.h"
23 #include "aosd_cfg.h"
24 
25 #include <libaudcore/i18n.h>
26 #include <X11/Xlib.h>
27 #include <cairo/cairo.h>
28 #include <pango/pangocairo.h>
29 #include "ghosd.h"
30 
31 
32 /* HOW TO ADD A NEW DECORATION STYLE
33    --------------------------------------------------------------------------
34    First, add a new decoration style code; then, provide the decoration style
35    information by adding a new entry in the aosd_deco_styles[] array (name,
36    render function, etc.); add the new decoration style code in the array
37    of decoration style codes, and update the define containing the array size.
38    The render function uses three parameters; the Ghosd instance, the cairo
39    object you use to draw, and a aosd_deco_style_data_t object that contains
40    user-defined options (look into aosd_style.h and aosd_cfg.h for details).
41    Have fun! :)
42 */
43 
44 /* prototypes of render functions */
45 static void aosd_deco_rfunc_rect ( Ghosd * , cairo_t * , aosd_deco_style_data_t * );
46 static void aosd_deco_rfunc_roundrect ( Ghosd * , cairo_t * , aosd_deco_style_data_t * );
47 static void aosd_deco_rfunc_concaverect ( Ghosd * , cairo_t * , aosd_deco_style_data_t * );
48 static void aosd_deco_rfunc_none ( Ghosd * , cairo_t * , aosd_deco_style_data_t * );
49 
50 /* map decoration style codes to decoration objects */
51 aosd_deco_style_t aosd_deco_styles[] =
52 {
53   // AOSD_DECO_STYLE_RECT
54   { N_("Rectangle") ,
55     aosd_deco_rfunc_rect ,
56     2 , { 10 , 10 , 10 , 10 } },
57 
58   // AOSD_DECO_STYLE_ROUNDRECT
59   { N_("Rounded Rectangle") ,
60     aosd_deco_rfunc_roundrect ,
61     2 , { 10 , 10 , 10 , 10 } },
62 
63   // AOSD_DECO_STYLE_CONCAVERECT
64   { N_("Concave Rectangle") ,
65     aosd_deco_rfunc_concaverect ,
66     2 , { 10 , 10 , 10 , 10 } },
67 
68   // AOSD_DECO_STYLE_NONE
69   { N_("None") ,
70     aosd_deco_rfunc_none ,
71     0 , { 2 , 2 , 2 , 2 } }
72 };
73 
74 static_assert (aud::n_elems (aosd_deco_styles) == AOSD_NUM_DECO_STYLES, "update aosd_deco_styles");
75 
76 
77 /* DECORATION STYLE API */
78 
79 void
aosd_deco_style_get_padding(int deco_code,int * ptop,int * pbottom,int * pleft,int * pright)80 aosd_deco_style_get_padding ( int deco_code ,
81                               int * ptop , int * pbottom ,
82                               int * pleft , int * pright )
83 {
84   if ( ptop != nullptr ) *ptop = aosd_deco_styles[deco_code].padding.top;
85   if ( pbottom != nullptr ) *pbottom = aosd_deco_styles[deco_code].padding.bottom;
86   if ( pleft != nullptr ) *pleft = aosd_deco_styles[deco_code].padding.left;
87   if ( pright != nullptr ) *pright = aosd_deco_styles[deco_code].padding.right;
88   return;
89 }
90 
91 
92 const char *
aosd_deco_style_get_desc(int deco_code)93 aosd_deco_style_get_desc ( int deco_code )
94 {
95   return aosd_deco_styles[deco_code].desc;
96 }
97 
98 
99 int
aosd_deco_style_get_numcol(int deco_code)100 aosd_deco_style_get_numcol ( int deco_code )
101 {
102   return aosd_deco_styles[deco_code].colors_num;
103 }
104 
105 
106 void
aosd_deco_style_render(int deco_code,void * ghosd,void * cr,void * user_data)107 aosd_deco_style_render ( int deco_code , void * ghosd , void * cr , void * user_data )
108 {
109   aosd_deco_styles[deco_code].render_func ((Ghosd *) ghosd, (cairo_t *) cr,
110    (aosd_deco_style_data_t *) user_data);
111 }
112 
113 
114 // sizing helper
115 static void
aosd_layout_size(PangoLayout * layout,int * width,int * height,int * bearing)116 aosd_layout_size( PangoLayout * layout , int * width , int * height , int * bearing )
117 {
118   PangoRectangle ink, log;
119 
120   pango_layout_get_pixel_extents( layout , &ink , &log );
121 
122   if ( width != nullptr )
123     *width = ink.width;
124   if ( height != nullptr )
125     *height = log.height;
126   if ( bearing != nullptr )
127     *bearing = -ink.x;
128 }
129 
130 
131 /* RENDER FUNCTIONS */
132 
133 static void
aosd_deco_rfunc_rect(Ghosd * osd,cairo_t * cr,aosd_deco_style_data_t * data)134 aosd_deco_rfunc_rect( Ghosd * osd , cairo_t * cr , aosd_deco_style_data_t * data )
135 {
136   /* decoration information
137      ----------------------
138      draws a simple rectangular decoration; uses 2 colors
139      (user color 1 and 2) and 1 font (user font 1), with optional shadow
140   */
141   PangoLayout *osd_layout = data->layout;
142   aosd_color_t color0 = data->decoration->colors[0];
143   aosd_color_t color1 = data->decoration->colors[1];
144   aosd_color_t textcolor0 = data->text->fonts_color[0];
145   aosd_color_t shadowcolor0 = data->text->fonts_shadow_color[0];
146   gboolean draw_shadow = data->text->fonts_draw_shadow[0];
147   int width = 0, height = 0, bearing = 0;
148 
149   aosd_layout_size( osd_layout , &width , &height , &bearing );
150 
151   /* draw rectangle container */
152   cairo_set_source_rgba( cr , (double)color0.red / 65535 , (double)color0.green / 65535 ,
153     (double)color0.blue / 65535 , (double)color0.alpha / 65535 );
154   cairo_rectangle( cr , 0 , 0 ,
155     aosd_deco_styles[AOSD_DECO_STYLE_RECT].padding.left + width +
156     aosd_deco_styles[AOSD_DECO_STYLE_RECT].padding.right,
157     aosd_deco_styles[AOSD_DECO_STYLE_RECT].padding.top + height +
158     aosd_deco_styles[AOSD_DECO_STYLE_RECT].padding.bottom );
159   cairo_fill_preserve( cr );
160   cairo_set_source_rgba( cr , (double)color1.red / 65535 , (double)color1.green / 65535 ,
161     (double)color1.blue / 65535 , (double)color1.alpha / 65535 );
162   cairo_stroke( cr );
163 
164   if ( draw_shadow == true )
165   {
166     /* draw text shadow */
167     cairo_set_source_rgba( cr , (double)shadowcolor0.red / 65535 , (double)shadowcolor0.green / 65535 ,
168       (double)shadowcolor0.blue / 65535 , (double)shadowcolor0.alpha / 65535 );
169     cairo_move_to( cr,
170       aosd_deco_styles[AOSD_DECO_STYLE_RECT].padding.left + bearing + 2 ,
171       aosd_deco_styles[AOSD_DECO_STYLE_RECT].padding.top + 2 );
172     pango_cairo_show_layout( cr , osd_layout );
173   }
174 
175   /* draw text */
176   cairo_set_source_rgba( cr , (double)textcolor0.red / 65535 , (double)textcolor0.green / 65535 ,
177     (double)textcolor0.blue / 65535 , (double)textcolor0.alpha / 65535 );
178   cairo_move_to( cr,
179     aosd_deco_styles[AOSD_DECO_STYLE_RECT].padding.left + bearing ,
180     aosd_deco_styles[AOSD_DECO_STYLE_RECT].padding.top );
181   pango_cairo_show_layout( cr , osd_layout );
182 }
183 
184 
185 static void
aosd_deco_rfunc_roundrect(Ghosd * osd,cairo_t * cr,aosd_deco_style_data_t * data)186 aosd_deco_rfunc_roundrect ( Ghosd * osd , cairo_t * cr , aosd_deco_style_data_t * data )
187 {
188   /* decoration information
189      ----------------------
190      draws a rectangular decoration with rounded angles; uses 2 colors
191      (user color 1 and 2) and 1 font (user font 1), with optional shadow
192   */
193   PangoLayout *osd_layout = data->layout;
194   aosd_color_t color0 = data->decoration->colors[0];
195   aosd_color_t color1 = data->decoration->colors[1];
196   aosd_color_t textcolor0 = data->text->fonts_color[0];
197   aosd_color_t shadowcolor0 = data->text->fonts_shadow_color[0];
198   gboolean draw_shadow = data->text->fonts_draw_shadow[0];
199   int width = 0, height = 0, bearing = 0;
200 
201   aosd_layout_size( osd_layout , &width , &height , &bearing );
202 
203   /* draw rounded-rectangle container */
204   cairo_set_source_rgba( cr , (double)color0.red / 65535 , (double)color0.green / 65535 ,
205     (double)color0.blue / 65535 , (double)color0.alpha / 65535 );
206   cairo_move_to( cr , aosd_deco_styles[AOSD_DECO_STYLE_ROUNDRECT].padding.left , 0 );
207   cairo_arc( cr , width + aosd_deco_styles[AOSD_DECO_STYLE_ROUNDRECT].padding.left ,
208     aosd_deco_styles[AOSD_DECO_STYLE_ROUNDRECT].padding.top ,
209     10. , -G_PI_2 , 0. );
210   cairo_arc( cr , width + aosd_deco_styles[AOSD_DECO_STYLE_ROUNDRECT].padding.left ,
211     aosd_deco_styles[AOSD_DECO_STYLE_ROUNDRECT].padding.top + height ,
212     10. , -4. * G_PI_2 , -3. * G_PI_2 );
213   cairo_arc( cr , aosd_deco_styles[AOSD_DECO_STYLE_ROUNDRECT].padding.left ,
214     aosd_deco_styles[AOSD_DECO_STYLE_ROUNDRECT].padding.top + height ,
215     10. , -3. * G_PI_2 , -2. * G_PI_2 );
216   cairo_arc( cr , aosd_deco_styles[AOSD_DECO_STYLE_ROUNDRECT].padding.left ,
217     aosd_deco_styles[AOSD_DECO_STYLE_ROUNDRECT].padding.top ,
218     10. , -2. * G_PI_2 , -G_PI_2 );
219   cairo_close_path( cr );
220   cairo_fill_preserve( cr );
221   cairo_set_source_rgba( cr , (double)color1.red / 65535 , (double)color1.green / 65535 ,
222     (double)color1.blue / 65535 , (double)color1.alpha / 65535 );
223   cairo_stroke( cr );
224 
225   if ( draw_shadow == true )
226   {
227     /* draw text shadow */
228     cairo_set_source_rgba( cr , (double)shadowcolor0.red / 65535 , (double)shadowcolor0.green / 65535 ,
229       (double)shadowcolor0.blue / 65535 , (double)shadowcolor0.alpha / 65535 );
230     cairo_move_to( cr ,
231       aosd_deco_styles[AOSD_DECO_STYLE_ROUNDRECT].padding.left + bearing + 2 ,
232       aosd_deco_styles[AOSD_DECO_STYLE_ROUNDRECT].padding.top + 2 );
233     pango_cairo_show_layout( cr , osd_layout );
234   }
235 
236   /* draw text */
237   cairo_set_source_rgba( cr , (double)textcolor0.red / 65535 , (double)textcolor0.green / 65535 ,
238     (double)textcolor0.blue / 65535 , (double)textcolor0.alpha / 65535 );
239   cairo_move_to( cr ,
240     aosd_deco_styles[AOSD_DECO_STYLE_ROUNDRECT].padding.left + bearing ,
241     aosd_deco_styles[AOSD_DECO_STYLE_ROUNDRECT].padding.top );
242   pango_cairo_show_layout( cr , osd_layout );
243 }
244 
245 
246 static void
aosd_deco_rfunc_concaverect(Ghosd * osd,cairo_t * cr,aosd_deco_style_data_t * data)247 aosd_deco_rfunc_concaverect ( Ghosd * osd , cairo_t * cr , aosd_deco_style_data_t * data )
248 {
249   /* decoration information
250      ----------------------
251      draws a rectangle with concave angles; uses 2 colors
252      (user color 1 and 2) and 1 font (user font 1), with optional shadow
253   */
254   PangoLayout *osd_layout = data->layout;
255   aosd_color_t color0 = data->decoration->colors[0];
256   aosd_color_t color1 = data->decoration->colors[1];
257   aosd_color_t textcolor0 = data->text->fonts_color[0];
258   aosd_color_t shadowcolor0 = data->text->fonts_shadow_color[0];
259   gboolean draw_shadow = data->text->fonts_draw_shadow[0];
260   int width = 0, height = 0, bearing = 0;
261 
262   aosd_layout_size( osd_layout , &width , &height , &bearing );
263 
264   /* draw jigsaw-piece-like container */
265   cairo_set_source_rgba( cr , (double)color0.red / 65535 , (double)color0.green / 65535 ,
266     (double)color0.blue / 65535 , (double)color0.alpha / 65535 );
267   cairo_move_to( cr , aosd_deco_styles[AOSD_DECO_STYLE_CONCAVERECT].padding.left , 0 );
268   cairo_arc_negative( cr , width + aosd_deco_styles[AOSD_DECO_STYLE_CONCAVERECT].padding.left + 2 ,
269     aosd_deco_styles[AOSD_DECO_STYLE_CONCAVERECT].padding.top - 2 ,
270     8. , -G_PI_2 , 0. );
271   cairo_arc_negative( cr , width + aosd_deco_styles[AOSD_DECO_STYLE_CONCAVERECT].padding.left + 2 ,
272     aosd_deco_styles[AOSD_DECO_STYLE_CONCAVERECT].padding.top + height + 2 ,
273     8. , -4. * G_PI_2 , -3. * G_PI_2 );
274   cairo_arc_negative( cr , aosd_deco_styles[AOSD_DECO_STYLE_CONCAVERECT].padding.left - 2 ,
275     aosd_deco_styles[AOSD_DECO_STYLE_CONCAVERECT].padding.top + height + 2 ,
276     8. , -3. * G_PI_2 , -2. * G_PI_2 );
277   cairo_arc_negative( cr , aosd_deco_styles[AOSD_DECO_STYLE_CONCAVERECT].padding.left - 2 ,
278     aosd_deco_styles[AOSD_DECO_STYLE_CONCAVERECT].padding.top - 2 ,
279     8. , -2. * G_PI_2 , -G_PI_2 );
280   cairo_close_path( cr );
281   cairo_fill_preserve( cr );
282   cairo_set_source_rgba( cr , (double)color1.red / 65535 , (double)color1.green / 65535 ,
283     (double)color1.blue / 65535 , (double)color1.alpha / 65535 );
284   cairo_stroke( cr );
285 
286   if ( draw_shadow == true )
287   {
288     /* draw text shadow */
289     cairo_set_source_rgba( cr , (double)shadowcolor0.red / 65535 , (double)shadowcolor0.green / 65535 ,
290       (double)shadowcolor0.blue / 65535 , (double)shadowcolor0.alpha / 65535 );
291     cairo_move_to( cr ,
292       aosd_deco_styles[AOSD_DECO_STYLE_CONCAVERECT].padding.left + bearing + 2 ,
293       aosd_deco_styles[AOSD_DECO_STYLE_CONCAVERECT].padding.top + 2 );
294     pango_cairo_show_layout( cr , osd_layout );
295   }
296 
297   /* draw text */
298   cairo_set_source_rgba( cr , (double)textcolor0.red / 65535 , (double)textcolor0.green / 65535 ,
299     (double)textcolor0.blue / 65535 , (double)textcolor0.alpha / 65535 );
300   cairo_move_to( cr ,
301     aosd_deco_styles[AOSD_DECO_STYLE_CONCAVERECT].padding.left + bearing ,
302     aosd_deco_styles[AOSD_DECO_STYLE_CONCAVERECT].padding.top );
303   pango_cairo_show_layout( cr , osd_layout );
304 }
305 
306 
307 static void
aosd_deco_rfunc_none(Ghosd * osd,cairo_t * cr,aosd_deco_style_data_t * data)308 aosd_deco_rfunc_none ( Ghosd * osd , cairo_t * cr , aosd_deco_style_data_t * data )
309 {
310   /* decoration information
311      ----------------------
312      does not draw any decoration around text; uses 0 colors
313      and 1 font (user font 1), with optional shadow
314   */
315   PangoLayout *osd_layout = data->layout;
316   aosd_color_t textcolor0 = data->text->fonts_color[0];
317   aosd_color_t shadowcolor0 = data->text->fonts_shadow_color[0];
318   gboolean draw_shadow = data->text->fonts_draw_shadow[0];
319   int width = 0, height = 0, bearing = 0;
320 
321   aosd_layout_size( osd_layout , &width , &height , &bearing );
322 
323   if ( draw_shadow == true )
324   {
325     /* draw text shadow */
326     cairo_set_source_rgba( cr , (double)shadowcolor0.red / 65535 , (double)shadowcolor0.green / 65535 ,
327       (double)shadowcolor0.blue / 65535 , (double)shadowcolor0.alpha / 65535 );
328     cairo_move_to( cr ,
329       aosd_deco_styles[AOSD_DECO_STYLE_NONE].padding.left + bearing + 2 ,
330       aosd_deco_styles[AOSD_DECO_STYLE_NONE].padding.top + 2 );
331     pango_cairo_show_layout( cr , osd_layout );
332   }
333 
334   /* draw text */
335   cairo_set_source_rgba( cr , (double)textcolor0.red / 65535 , (double)textcolor0.green / 65535 ,
336     (double)textcolor0.blue / 65535 , (double)textcolor0.alpha / 65535 );
337   cairo_move_to( cr ,
338     aosd_deco_styles[AOSD_DECO_STYLE_NONE].padding.left + bearing ,
339     aosd_deco_styles[AOSD_DECO_STYLE_NONE].padding.top );
340   pango_cairo_show_layout( cr , osd_layout );
341 }
342