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