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