File indexing completed on 2024-05-05 05:34:56
0001 /* 0002 this file is part of the oxygen gtk engine 0003 SPDX-FileCopyrightText: 2010 Hugo Pereira Da Costa <hugo.pereira@free.fr> 0004 SPDX-FileCopyrightText: 2010 Ruslan Kabatsayev <b7.10110111@gmail.com> 0005 0006 based on the Null Theme Engine for Gtk+. 0007 SPDX-FileCopyrightText: 2008 Robert Staudinger <robert.staudinger@gmail.com> 0008 0009 SPDX-License-Identifier: LGPL-2.0-or-later 0010 */ 0011 0012 #include <gtk/gtk.h> 0013 #include <dlfcn.h> 0014 #include <string> 0015 #include "oxygenversion.h" 0016 0017 enum WinDecoOptions 0018 { 0019 WinIsMaximized=1<<0, 0020 WinIsShaded=1<<2, 0021 WinIsResizable=1<<3, 0022 WinIsActive=1<<4, 0023 WinHasAlpha=1<<5, 0024 DrawAlphaChannel=1<<6, 0025 }; 0026 0027 //! button status 0028 enum ButtonStatus 0029 { 0030 Normal, 0031 Disabled, // this shouldn't be specified by WMs unless button is really insensitive 0032 Hovered, 0033 Pressed, 0034 ButtonStatusCount 0035 }; 0036 0037 //! buttons 0038 enum ButtonType 0039 { 0040 ButtonHelp=0, 0041 ButtonMax, 0042 ButtonMin, 0043 ButtonClose, 0044 ButtonMenu, 0045 ButtonSticky, 0046 ButtonAbove, 0047 ButtonBelow, 0048 ButtonShade, 0049 ButtonUnmax, 0050 ButtonUnstick, 0051 ButtonUnshade, 0052 ButtonUndoAbove, 0053 ButtonUndoBelow, 0054 ButtonTypeCount 0055 }; 0056 0057 enum Metric 0058 { 0059 BorderLeft=0, 0060 BorderRight, 0061 BorderBottom, 0062 // BorderTop includes title and resize handle heights 0063 BorderTop, 0064 ButtonSpacing, 0065 ButtonMarginTop, 0066 ButtonMarginBottom, 0067 ShadowLeft, 0068 ShadowTop, 0069 ShadowRight, 0070 ShadowBottom, 0071 MetricsCount 0072 }; 0073 0074 //___________________________________________________________________ 0075 // external pointers to functions 0076 void (*drawWindowDecoration)(cairo_t*, unsigned long, gint, gint, gint, gint, const gchar**, gint, gint) = 0L; 0077 void (*drawWindecoButton)(cairo_t*, unsigned long, unsigned long, unsigned long, gint, gint, gint, gint) = 0L; 0078 void (*drawWindecoShapeMask)(cairo_t*, unsigned long, gint, gint, gint, gint) = 0L; 0079 void (*drawWindowShadow)(cairo_t*, unsigned long, gint, gint, gint, gint) = 0L; 0080 gint (*getWindecoABIVersion)(void) = 0L; 0081 gint (*getWindecoMetric)(unsigned long) = 0L; 0082 gint (*getWindecoButtonSize)(unsigned long) = 0L; 0083 0084 // window dimensions 0085 int ww( 700 ); 0086 int wh( 200 ); 0087 0088 // widgets 0089 GtkWidget *mw0( 0L ); 0090 GtkWidget *mw1( 0L ); 0091 0092 const gboolean useAlpha( TRUE ); 0093 0094 // windeco metrics 0095 int shadowLeft=0; 0096 int shadowRight=0; 0097 int shadowTop=0; 0098 int shadowBottom=0; 0099 int borderLeft=0; 0100 int borderRight=0; 0101 int borderTop=0; 0102 int borderBottom=0; 0103 int dw=0; 0104 int dh=0; 0105 0106 //___________________________________________________________________ 0107 gboolean initLib() 0108 { 0109 void* library; 0110 char* error=0; 0111 char* moduleDir=gtk_rc_get_module_dir(); 0112 if(moduleDir) 0113 { 0114 std::string libpath(moduleDir+std::string("/liboxygen-gtk.so")); 0115 g_free(moduleDir); 0116 library=dlopen(libpath.c_str(),RTLD_LAZY); 0117 bool success=false; 0118 do{ 0119 if(!library) 0120 { 0121 break; 0122 } else { 0123 0124 getWindecoABIVersion=(gint(*)(void))dlsym(library, "getWindecoABIVersion"); 0125 if((error=dlerror())!=0L) 0126 { 0127 break; 0128 } else { 0129 0130 #define EXPECTED_ABI_VERSION 3 0131 gint version=getWindecoABIVersion(); 0132 if(version != EXPECTED_ABI_VERSION) 0133 { 0134 fprintf(stderr, "ABI version %d is not equal to expected version %d\n", version, EXPECTED_ABI_VERSION); 0135 return FALSE; 0136 } 0137 0138 } 0139 0140 // store drawWindowDecoration symbol 0141 drawWindowDecoration = (void(*)(cairo_t*, unsigned long, gint, gint, gint, gint, const gchar**, gint, gint))dlsym(library, "drawWindowDecoration"); 0142 if((error=dlerror())!=0L) 0143 break; 0144 0145 // store drawWindecoButton symbol 0146 drawWindecoButton = (void (*)(cairo_t*, unsigned long, unsigned long, unsigned long, gint, gint, gint, gint))dlsym(library, "drawWindecoButton"); 0147 if((error=dlerror())!=0L) 0148 break; 0149 0150 // store drawWindecoShapeMask symbol 0151 drawWindecoShapeMask=(void (*)(cairo_t*, unsigned long, gint, gint, gint, gint))dlsym(library, "drawWindecoShapeMask"); 0152 if((error=dlerror())!=0L) 0153 break; 0154 0155 // store drawWindowShadow symbol 0156 drawWindowShadow=(void (*)(cairo_t*, unsigned long, gint, gint, gint, gint))dlsym(library, "drawWindowShadow"); 0157 if((error=dlerror())!=0L) 0158 break; 0159 0160 // store drawWindecoMetric symbol 0161 getWindecoMetric=(gint (*)(unsigned long))dlsym(library, "getWindecoMetric"); 0162 if((error=dlerror())!=0L) 0163 break; 0164 0165 // store drawWindecoButtonSize symbol 0166 getWindecoButtonSize=(gint (*)(unsigned long))dlsym(library, "getWindecoButtonSize"); 0167 if((error=dlerror())!=0L) 0168 break; 0169 } 0170 } 0171 while(0); 0172 if(error) 0173 { 0174 fprintf(stderr, "%s\n", error); 0175 return FALSE; 0176 } 0177 } 0178 return TRUE; 0179 } 0180 0181 void getMetrics() 0182 { 0183 shadowLeft=getWindecoMetric(ShadowLeft); 0184 shadowRight=getWindecoMetric(ShadowRight); 0185 shadowTop=getWindecoMetric(ShadowTop); 0186 shadowBottom=getWindecoMetric(ShadowBottom); 0187 0188 borderLeft=getWindecoMetric(BorderLeft); 0189 borderRight=getWindecoMetric(BorderRight); 0190 borderTop=getWindecoMetric(BorderTop); 0191 borderBottom=getWindecoMetric(BorderBottom); 0192 0193 dw = borderLeft + borderRight + shadowLeft + shadowRight; 0194 dh = borderTop + borderBottom + shadowTop + shadowBottom; 0195 } 0196 0197 //___________________________________________________________________ 0198 gboolean on_expose(GtkWidget* mw0, GdkEventExpose* event, gpointer user_data) 0199 { 0200 cairo_t* cr=gdk_cairo_create( gtk_widget_get_window( mw0 ) ); 0201 cairo_set_antialias( cr, CAIRO_ANTIALIAS_SUBPIXEL ); 0202 0203 // define options 0204 int opt( WinIsResizable|DrawAlphaChannel ); 0205 if( useAlpha ) opt |= WinHasAlpha; 0206 if( !gtk_window_is_active( GTK_WINDOW(mw1) ) ) opt|= WinIsActive; 0207 0208 drawWindowShadow(cr, opt, 0, 0, mw0->allocation.width, mw0->allocation.height); 0209 0210 const gchar* windowStrings[] = { 0211 "This is a caption", 0212 "WindowClass10110111", 0213 0 }; 0214 0215 drawWindowDecoration(cr, opt, 0+shadowLeft, 0+shadowTop, mw0->allocation.width-shadowLeft-shadowRight, mw0->allocation.height-shadowTop-shadowBottom, windowStrings, 0, 20*ButtonTypeCount); 0216 0217 for( int status=0; status<ButtonStatusCount; status++) 0218 { 0219 for( int type=0; type<ButtonTypeCount; type++) 0220 { 0221 int buttonSize=getWindecoButtonSize(type); 0222 int buttonSpacing=getWindecoMetric(ButtonSpacing); 0223 int dbut=buttonSize+buttonSpacing; 0224 drawWindecoButton(cr, type, status, opt, 0225 mw0->allocation.width-shadowRight-borderRight-buttonSize-buttonSize*type, 0226 status*dbut+shadowTop+(borderTop-buttonSize)/2, 0227 buttonSize, buttonSize); 0228 } 0229 } 0230 0231 cairo_destroy(cr); 0232 return TRUE; 0233 } 0234 0235 //___________________________________________________________________ 0236 gboolean on_configure1(GtkWidget* mw1, GdkEventConfigure* event, gpointer user_data) 0237 { 0238 static int w=0, h=0; 0239 gboolean redraw( event->width != w || event->height != h ); 0240 if( redraw ) 0241 { 0242 w=event->width; 0243 h=event->height; 0244 0245 int opt( WinIsActive|WinIsResizable ); 0246 if( useAlpha ) opt|= WinHasAlpha; 0247 0248 gtk_window_resize(GTK_WINDOW(mw0), event->width+dw, event->height+dh); 0249 0250 if(!useAlpha) 0251 { 0252 GdkBitmap* mask=gdk_pixmap_new(0L, event->width+dw, event->height+dh, 1); 0253 cairo_t* cr=gdk_cairo_create(mask); 0254 drawWindecoShapeMask(cr, opt, 0+shadowLeft, 0+shadowRight, event->width+dw-shadowLeft-shadowRight, event->height+dh-shadowTop-shadowBottom); 0255 gdk_window_shape_combine_mask( gtk_widget_get_window( mw0 ), 0L, 0, 0 ); // remove old mask 0256 gdk_window_shape_combine_mask( gtk_widget_get_window( mw0 ), mask, 0, 0 ); // apply new mask 0257 gdk_pixmap_unref(mask); 0258 } 0259 } 0260 0261 return FALSE; 0262 0263 } 0264 0265 //___________________________________________________________________ 0266 gboolean on_configure0(GtkWidget* mw0, GdkEventConfigure* event, gpointer user_data) 0267 { 0268 static int w=0, h=0; 0269 static gboolean active=FALSE; 0270 0271 gboolean redraw( event->width != w || event->height != h ); 0272 if(!(gtk_window_is_active(GTK_WINDOW(mw1))!=active)) 0273 { 0274 active ^= TRUE; 0275 redraw = TRUE; 0276 } 0277 0278 if( redraw ) 0279 { 0280 w=event->width; 0281 h=event->height; 0282 0283 gdk_window_begin_paint_rect( gtk_widget_get_window( mw0 ), (GdkRectangle*)&mw0->allocation); 0284 on_expose(mw0, 0L, 0L ); 0285 gdk_window_end_paint( gtk_widget_get_window( mw0 ) ); 0286 } 0287 0288 return FALSE; 0289 0290 } 0291 0292 //___________________________________________________________________ 0293 gboolean on_press0(GtkWindow* window, GdkEventButton* event, GdkWindowEdge edge) 0294 { 0295 if(event->type == GDK_BUTTON_PRESS) 0296 if(event->button == 1) 0297 gtk_window_begin_move_drag(window,event->button,event->x_root,event->y_root,event->time); 0298 return FALSE; 0299 } 0300 0301 //___________________________________________________________________ 0302 int main( int argc, char** argv ) 0303 { 0304 0305 // load methods from style library 0306 if(!initLib()) return 1; 0307 0308 // initialize gtk 0309 gtk_init(&argc, &argv); 0310 0311 // command line arguments 0312 gboolean version( FALSE ); 0313 GOptionEntry entries[] = 0314 { 0315 { "version", 'v', 0, G_OPTION_ARG_NONE, &version, "Show the application's version", 0L }, 0316 { 0L } 0317 }; 0318 0319 GError *error = 0L; 0320 GOptionContext* context( g_option_context_new( "- Gtk+ widgets preview for oxygen" ) ); 0321 g_option_context_add_main_entries(context, entries, 0L ); 0322 g_option_context_add_group (context, gtk_get_option_group( TRUE ) ); 0323 g_option_context_parse( context, &argc, &argv, &error ); 0324 g_option_context_free( context ); 0325 0326 if( version ) 0327 { 0328 /* 0329 HACK: need to create (and destroy immediatly) a dummy window 0330 in order to make sure that the widget style is initialized properly 0331 */ 0332 GtkWidget* window = gtk_window_new( GTK_WINDOW_TOPLEVEL ); 0333 gtk_widget_destroy( window ); 0334 0335 Oxygen::Version::print(); 0336 return 0; 0337 } 0338 0339 // draw 0340 mw0=gtk_window_new(GTK_WINDOW_TOPLEVEL); 0341 mw1=gtk_window_new(GTK_WINDOW_TOPLEVEL); 0342 0343 GdkColormap* cmap=gdk_screen_get_rgba_colormap(gdk_screen_get_default()); 0344 gtk_widget_set_colormap(mw0, cmap); 0345 0346 g_signal_connect(G_OBJECT(mw0), "destroy", G_CALLBACK(gtk_main_quit), 0L); 0347 g_signal_connect(G_OBJECT(mw1), "destroy", G_CALLBACK(gtk_main_quit), 0L); 0348 0349 gtk_window_set_default_size( GTK_WINDOW(mw1), ww, wh ); 0350 gtk_window_set_default_size( GTK_WINDOW(mw0), ww+dw, wh+dh ); 0351 gtk_window_set_decorated(GTK_WINDOW(mw0), FALSE); 0352 gtk_widget_add_events(mw0,GDK_BUTTON_PRESS_MASK); 0353 0354 gtk_window_set_title( GTK_WINDOW(mw1), "This is a caption"); 0355 0356 g_signal_connect( G_OBJECT(mw0), "expose-event", G_CALLBACK(on_expose), 0L); 0357 g_signal_connect( G_OBJECT(mw1), "configure-event", G_CALLBACK(on_configure1), 0L); 0358 g_signal_connect( G_OBJECT(mw0), "configure-event", G_CALLBACK(on_configure0), 0L); 0359 g_signal_connect( G_OBJECT(mw0), "button-press-event", G_CALLBACK(on_press0), NULL); 0360 0361 gtk_widget_show( mw1 ); 0362 gtk_widget_show( mw0 ); 0363 0364 gtk_window_move( GTK_WINDOW(mw0), 300, 320); 0365 gtk_window_move( GTK_WINDOW(mw1), 300, 320); 0366 0367 // FIXME: this call crashes in Oxygen::StyleHelper::initializeRefSurface() 0368 // if done right after gtk_init() call, i.e. until any widget is created 0369 getMetrics(); 0370 0371 gtk_main(); 0372 return 0; 0373 }