1 /*
2 * this file is part of the oxygen gtk engine
3 * Copyright (c) 2010 Hugo Pereira Da Costa <hugo.pereira@free.fr>
4 * Copyright (c) 2010 Ruslan Kabatsayev <b7.10110111@gmail.com>
5 *
6 * based on the Null Theme Engine for Gtk+.
7 * Copyright (c) 2008 Robert Staudinger <robert.staudinger@gmail.com>
8 *
9 * This  library is free  software; you can  redistribute it and/or
10 * modify it  under  the terms  of the  GNU Lesser  General  Public
11 * License  as published  by the Free  Software  Foundation; either
12 * version 2 of the License, or( at your option ) any later version.
13 *
14 * This library is distributed  in the hope that it will be useful,
15 * but  WITHOUT ANY WARRANTY; without even  the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License  along  with  this library;  if not,  write to  the Free
21 * Software Foundation, Inc., 51  Franklin St, Fifth Floor, Boston,
22 * MA 02110-1301, USA.
23 */
24 
25 #include <gtk/gtk.h>
26 #include <dlfcn.h>
27 #include <string>
28 #include "oxygenversion.h"
29 
30 enum WinDecoOptions
31 {
32     WinIsMaximized=1<<0,
33     WinIsShaded=1<<2,
34     WinIsResizable=1<<3,
35     WinIsActive=1<<4,
36     WinHasAlpha=1<<5,
37     DrawAlphaChannel=1<<6,
38 };
39 
40 //! button status
41 enum ButtonStatus
42 {
43     Normal,
44     Disabled, // this shouldn't be specified by WMs unless button is really insensitive
45     Hovered,
46     Pressed,
47     ButtonStatusCount
48 };
49 
50 //! buttons
51 enum ButtonType
52 {
53     ButtonHelp=0,
54     ButtonMax,
55     ButtonMin,
56     ButtonClose,
57     ButtonMenu,
58     ButtonSticky,
59     ButtonAbove,
60     ButtonBelow,
61     ButtonShade,
62     ButtonUnmax,
63     ButtonUnstick,
64     ButtonUnshade,
65     ButtonUndoAbove,
66     ButtonUndoBelow,
67     ButtonTypeCount
68 };
69 
70 enum Metric
71 {
72     BorderLeft=0,
73     BorderRight,
74     BorderBottom,
75     // BorderTop includes title and resize handle heights
76     BorderTop,
77     ButtonSpacing,
78     ButtonMarginTop,
79     ButtonMarginBottom,
80     ShadowLeft,
81     ShadowTop,
82     ShadowRight,
83     ShadowBottom,
84     MetricsCount
85 };
86 
87 //___________________________________________________________________
88 // external pointers to functions
89 void (*drawWindowDecoration)(cairo_t*, unsigned long, gint, gint, gint, gint, const gchar**, gint, gint) = 0L;
90 void (*drawWindecoButton)(cairo_t*, unsigned long, unsigned long, unsigned long, gint, gint, gint, gint) = 0L;
91 void (*drawWindecoShapeMask)(cairo_t*, unsigned long, gint, gint, gint, gint) = 0L;
92 void (*drawWindowShadow)(cairo_t*, unsigned long, gint, gint, gint, gint) = 0L;
93 gint (*getWindecoABIVersion)(void) = 0L;
94 gint (*getWindecoMetric)(unsigned long) = 0L;
95 gint (*getWindecoButtonSize)(unsigned long) = 0L;
96 
97 // window dimensions
98 int ww( 700 );
99 int wh( 200 );
100 
101 // widgets
102 GtkWidget *mw0( 0L );
103 GtkWidget *mw1( 0L );
104 
105 const gboolean useAlpha( TRUE );
106 
107 // windeco metrics
108 int shadowLeft=0;
109 int shadowRight=0;
110 int shadowTop=0;
111 int shadowBottom=0;
112 int borderLeft=0;
113 int borderRight=0;
114 int borderTop=0;
115 int borderBottom=0;
116 int dw=0;
117 int dh=0;
118 
119 //___________________________________________________________________
initLib()120 gboolean initLib()
121 {
122     void* library;
123     char* error=0;
124     char* moduleDir=gtk_rc_get_module_dir();
125     if(moduleDir)
126     {
127         std::string libpath(moduleDir+std::string("/liboxygen-gtk.so"));
128         g_free(moduleDir);
129         library=dlopen(libpath.c_str(),RTLD_LAZY);
130         bool success=false;
131         do{
132             if(!library)
133             {
134                 break;
135             } else {
136 
137                 getWindecoABIVersion=(gint(*)(void))dlsym(library, "getWindecoABIVersion");
138                 if((error=dlerror())!=0L)
139                 {
140                     break;
141                 } else {
142 
143                     #define EXPECTED_ABI_VERSION 3
144                     gint version=getWindecoABIVersion();
145                     if(version != EXPECTED_ABI_VERSION)
146                     {
147                         fprintf(stderr, "ABI version %d is not equal to expected version %d\n", version, EXPECTED_ABI_VERSION);
148                         return FALSE;
149                     }
150 
151                 }
152 
153                 // store drawWindowDecoration symbol
154                 drawWindowDecoration = (void(*)(cairo_t*, unsigned long, gint, gint, gint, gint, const gchar**, gint, gint))dlsym(library, "drawWindowDecoration");
155                 if((error=dlerror())!=0L)
156                     break;
157 
158                 // store drawWindecoButton symbol
159                 drawWindecoButton = (void (*)(cairo_t*, unsigned long, unsigned long, unsigned long, gint, gint, gint, gint))dlsym(library, "drawWindecoButton");
160                 if((error=dlerror())!=0L)
161                     break;
162 
163                 // store drawWindecoShapeMask symbol
164                 drawWindecoShapeMask=(void (*)(cairo_t*, unsigned long, gint, gint, gint, gint))dlsym(library, "drawWindecoShapeMask");
165                 if((error=dlerror())!=0L)
166                     break;
167 
168                 // store drawWindowShadow symbol
169                 drawWindowShadow=(void (*)(cairo_t*, unsigned long, gint, gint, gint, gint))dlsym(library, "drawWindowShadow");
170                 if((error=dlerror())!=0L)
171                     break;
172 
173                 // store drawWindecoMetric symbol
174                 getWindecoMetric=(gint (*)(unsigned long))dlsym(library, "getWindecoMetric");
175                 if((error=dlerror())!=0L)
176                     break;
177 
178                 // store drawWindecoButtonSize symbol
179                 getWindecoButtonSize=(gint (*)(unsigned long))dlsym(library, "getWindecoButtonSize");
180                 if((error=dlerror())!=0L)
181                     break;
182               }
183         }
184         while(0);
185         if(error)
186         {
187             fprintf(stderr, "%s\n", error);
188             return FALSE;
189         }
190     }
191     return TRUE;
192 }
193 
getMetrics()194 void getMetrics()
195 {
196     shadowLeft=getWindecoMetric(ShadowLeft);
197     shadowRight=getWindecoMetric(ShadowRight);
198     shadowTop=getWindecoMetric(ShadowTop);
199     shadowBottom=getWindecoMetric(ShadowBottom);
200 
201     borderLeft=getWindecoMetric(BorderLeft);
202     borderRight=getWindecoMetric(BorderRight);
203     borderTop=getWindecoMetric(BorderTop);
204     borderBottom=getWindecoMetric(BorderBottom);
205 
206     dw = borderLeft + borderRight + shadowLeft + shadowRight;
207     dh = borderTop + borderBottom + shadowTop + shadowBottom;
208 }
209 
210 //___________________________________________________________________
on_expose(GtkWidget * mw0,GdkEventExpose * event,gpointer user_data)211 gboolean on_expose(GtkWidget* mw0, GdkEventExpose* event, gpointer user_data)
212 {
213     cairo_t* cr=gdk_cairo_create( gtk_widget_get_window( mw0 ) );
214     cairo_set_antialias( cr, CAIRO_ANTIALIAS_SUBPIXEL );
215 
216     // define options
217     int opt( WinIsResizable|DrawAlphaChannel );
218     if( useAlpha ) opt |= WinHasAlpha;
219     if( !gtk_window_is_active( GTK_WINDOW(mw1) ) ) opt|= WinIsActive;
220 
221     drawWindowShadow(cr, opt, 0, 0, mw0->allocation.width, mw0->allocation.height);
222 
223     const gchar* windowStrings[] = {
224         "This is a caption",
225         "WindowClass10110111",
226         0 };
227 
228     drawWindowDecoration(cr, opt, 0+shadowLeft, 0+shadowTop, mw0->allocation.width-shadowLeft-shadowRight, mw0->allocation.height-shadowTop-shadowBottom, windowStrings, 0, 20*ButtonTypeCount);
229 
230     for( int status=0; status<ButtonStatusCount; status++)
231     {
232         for( int type=0; type<ButtonTypeCount; type++)
233         {
234             int buttonSize=getWindecoButtonSize(type);
235             int buttonSpacing=getWindecoMetric(ButtonSpacing);
236             int dbut=buttonSize+buttonSpacing;
237             drawWindecoButton(cr, type, status, opt,
238                     mw0->allocation.width-shadowRight-borderRight-buttonSize-buttonSize*type,
239                     status*dbut+shadowTop+(borderTop-buttonSize)/2,
240                     buttonSize, buttonSize);
241         }
242     }
243 
244     cairo_destroy(cr);
245     return TRUE;
246 }
247 
248 //___________________________________________________________________
on_configure1(GtkWidget * mw1,GdkEventConfigure * event,gpointer user_data)249 gboolean on_configure1(GtkWidget* mw1, GdkEventConfigure* event, gpointer user_data)
250 {
251     static int w=0, h=0;
252     gboolean redraw( event->width != w || event->height != h );
253     if( redraw  )
254     {
255         w=event->width;
256         h=event->height;
257 
258         int opt( WinIsActive|WinIsResizable );
259         if( useAlpha ) opt|= WinHasAlpha;
260 
261         gtk_window_resize(GTK_WINDOW(mw0), event->width+dw, event->height+dh);
262 
263         if(!useAlpha)
264         {
265             GdkBitmap* mask=gdk_pixmap_new(0L, event->width+dw, event->height+dh, 1);
266             cairo_t* cr=gdk_cairo_create(mask);
267             drawWindecoShapeMask(cr, opt, 0+shadowLeft, 0+shadowRight, event->width+dw-shadowLeft-shadowRight, event->height+dh-shadowTop-shadowBottom);
268             gdk_window_shape_combine_mask( gtk_widget_get_window( mw0 ), 0L, 0, 0 ); // remove old mask
269             gdk_window_shape_combine_mask( gtk_widget_get_window( mw0 ), mask, 0, 0 ); // apply new mask
270             gdk_pixmap_unref(mask);
271         }
272     }
273 
274     return FALSE;
275 
276 }
277 
278 //___________________________________________________________________
on_configure0(GtkWidget * mw0,GdkEventConfigure * event,gpointer user_data)279 gboolean on_configure0(GtkWidget* mw0, GdkEventConfigure* event, gpointer user_data)
280 {
281     static int w=0, h=0;
282     static gboolean active=FALSE;
283 
284     gboolean redraw( event->width != w || event->height != h );
285     if(!(gtk_window_is_active(GTK_WINDOW(mw1))!=active))
286     {
287         active ^= TRUE;
288         redraw = TRUE;
289     }
290 
291     if( redraw )
292     {
293         w=event->width;
294         h=event->height;
295 
296         gdk_window_begin_paint_rect( gtk_widget_get_window( mw0 ), (GdkRectangle*)&mw0->allocation);
297         on_expose(mw0, 0L, 0L );
298         gdk_window_end_paint( gtk_widget_get_window( mw0 ) );
299     }
300 
301     return FALSE;
302 
303 }
304 
305 //___________________________________________________________________
on_press0(GtkWindow * window,GdkEventButton * event,GdkWindowEdge edge)306 gboolean on_press0(GtkWindow* window, GdkEventButton* event, GdkWindowEdge edge)
307 {
308     if(event->type == GDK_BUTTON_PRESS)
309         if(event->button == 1)
310             gtk_window_begin_move_drag(window,event->button,event->x_root,event->y_root,event->time);
311     return FALSE;
312 }
313 
314 //___________________________________________________________________
main(int argc,char ** argv)315 int main( int argc, char** argv )
316 {
317 
318     // load methods from style library
319     if(!initLib()) return 1;
320 
321     // initialize gtk
322     gtk_init(&argc, &argv);
323 
324     // command line arguments
325     gboolean version( FALSE );
326     GOptionEntry entries[] =
327     {
328         { "version", 'v', 0, G_OPTION_ARG_NONE, &version, "Show the application's version", 0L },
329         { 0L }
330     };
331 
332     GError *error = 0L;
333     GOptionContext* context( g_option_context_new( "- Gtk+ widgets preview for oxygen" ) );
334     g_option_context_add_main_entries(context, entries, 0L );
335     g_option_context_add_group (context, gtk_get_option_group( TRUE ) );
336     g_option_context_parse( context, &argc, &argv, &error );
337     g_option_context_free( context );
338 
339     if( version )
340     {
341         /*
342         HACK: need to create (and destroy immediatly) a dummy window
343         in order to make sure that the widget style is initialized properly
344         */
345         GtkWidget* window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
346         gtk_widget_destroy( window );
347 
348         Oxygen::Version::print();
349         return 0;
350     }
351 
352     // draw
353     mw0=gtk_window_new(GTK_WINDOW_TOPLEVEL);
354     mw1=gtk_window_new(GTK_WINDOW_TOPLEVEL);
355 
356     GdkColormap* cmap=gdk_screen_get_rgba_colormap(gdk_screen_get_default());
357     gtk_widget_set_colormap(mw0, cmap);
358 
359     g_signal_connect(G_OBJECT(mw0), "destroy", G_CALLBACK(gtk_main_quit), 0L);
360     g_signal_connect(G_OBJECT(mw1), "destroy", G_CALLBACK(gtk_main_quit), 0L);
361 
362     gtk_window_set_default_size( GTK_WINDOW(mw1), ww, wh );
363     gtk_window_set_default_size( GTK_WINDOW(mw0), ww+dw, wh+dh );
364     gtk_window_set_decorated(GTK_WINDOW(mw0), FALSE);
365     gtk_widget_add_events(mw0,GDK_BUTTON_PRESS_MASK);
366 
367     gtk_window_set_title( GTK_WINDOW(mw1), "This is a caption");
368 
369     g_signal_connect( G_OBJECT(mw0), "expose-event", G_CALLBACK(on_expose), 0L);
370     g_signal_connect( G_OBJECT(mw1), "configure-event", G_CALLBACK(on_configure1), 0L);
371     g_signal_connect( G_OBJECT(mw0), "configure-event", G_CALLBACK(on_configure0), 0L);
372     g_signal_connect( G_OBJECT(mw0), "button-press-event", G_CALLBACK(on_press0), NULL);
373 
374     gtk_widget_show( mw1 );
375     gtk_widget_show( mw0 );
376 
377     gtk_window_move( GTK_WINDOW(mw0), 300, 320);
378     gtk_window_move( GTK_WINDOW(mw1), 300, 320);
379 
380     // FIXME: this call crashes in Oxygen::StyleHelper::initializeRefSurface()
381     // if done right after gtk_init() call, i.e. until any widget is created
382     getMetrics();
383 
384     gtk_main();
385     return 0;
386 }
387