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 }