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