File indexing completed on 2024-05-05 17:40:44
0001 /* 0002 this file is part of the oxygen gtk engine 0003 SPDX-FileCopyrightText: 2011 Hugo Pereira Da Costa <hugo.pereira@free.fr> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "oxygencairocontext.h" 0009 #include "oxygencairoutils.h" 0010 #include "config.h" 0011 #include "oxygengtkutils.h" 0012 #include "oxygenmetrics.h" 0013 #include "oxygenrgba.h" 0014 #include "oxygenshadowhelper.h" 0015 0016 #include "config.h" 0017 0018 #include <cstring> 0019 #include <iostream> 0020 #include <cairo/cairo.h> 0021 0022 #ifdef GDK_WINDOWING_X11 0023 #include <cairo/cairo-xlib.h> 0024 #include <gdk/gdkx.h> 0025 #include <X11/Xatom.h> 0026 #endif 0027 0028 namespace Oxygen 0029 { 0030 0031 const char* const ShadowHelper::netWMShadowAtomName( "_KDE_NET_WM_SHADOW" ); 0032 0033 //______________________________________________ 0034 ShadowHelper::ShadowHelper( void ): 0035 _supported( false ), 0036 _size(0), 0037 _hooksInitialized( false ) 0038 { 0039 0040 #ifdef GDK_WINDOWING_X11 0041 _atom = None; 0042 #endif 0043 0044 #if OXYGEN_DEBUG 0045 std::cerr << "Oxygen::ShadowHelper::ShadowHelper" << std::endl; 0046 #endif 0047 } 0048 0049 //______________________________________________ 0050 ShadowHelper::~ShadowHelper( void ) 0051 { 0052 #if OXYGEN_DEBUG 0053 std::cerr << "Oxygen::ShadowHelper::~ShadowHelper" << std::endl; 0054 #endif 0055 0056 for( WidgetMap::iterator iter = _widgets.begin(); iter != _widgets.end(); ++iter ) 0057 { iter->second._destroyId.disconnect(); } 0058 0059 reset(); 0060 _realizeHook.disconnect(); 0061 } 0062 0063 //______________________________________________ 0064 void ShadowHelper::reset( void ) 0065 { 0066 0067 #if OXYGEN_DEBUG 0068 std::cerr << "Oxygen::ShadowHelper::reset" << std::endl; 0069 #endif 0070 0071 #ifdef GDK_WINDOWING_X11 0072 GdkScreen* screen = gdk_screen_get_default(); 0073 if( !screen ) return; 0074 0075 Display* display( GDK_DISPLAY_XDISPLAY( gdk_screen_get_display( screen ) ) ); 0076 0077 // round pixmaps 0078 for( PixmapList::const_iterator iter = _roundPixmaps.begin(); iter != _roundPixmaps.end(); ++iter ) 0079 { XFreePixmap(display, *iter); } 0080 0081 // square pixmaps 0082 for( PixmapList::const_iterator iter = _squarePixmaps.begin(); iter != _squarePixmaps.end(); ++iter ) 0083 { XFreePixmap(display, *iter); } 0084 #endif 0085 0086 // clear arrays 0087 _roundPixmaps.clear(); 0088 _squarePixmaps.clear(); 0089 0090 // reset size 0091 _size = 0; 0092 0093 } 0094 0095 //______________________________________________ 0096 void ShadowHelper::initializeHooks( void ) 0097 { 0098 if( _hooksInitialized ) return; 0099 0100 #if OXYGEN_DEBUG 0101 std::cerr << "Oxygen::ShadowHelper::initializeHooks" << std::endl; 0102 #endif 0103 0104 // install hooks 0105 _realizeHook.connect( "realize", (GSignalEmissionHook)realizeHook, this ); 0106 _hooksInitialized = true; 0107 0108 } 0109 0110 //______________________________________________ 0111 void ShadowHelper::initialize( const ColorUtils::Rgba& color, const WindowShadow& shadow ) 0112 { 0113 0114 #if OXYGEN_DEBUG 0115 std::cerr << "Oxygen::ShadowHelper::initialize" << std::endl; 0116 #endif 0117 0118 reset(); 0119 _size = int(shadow.shadowSize()) - WindowShadow::Overlap; 0120 0121 // round tiles 0122 WindowShadowKey key; 0123 key.hasTopBorder = true; 0124 key.hasBottomBorder = true; 0125 _roundTiles = shadow.tileSet( color, key ); 0126 0127 // square tiles 0128 key.hasTopBorder = false; 0129 key.hasBottomBorder = false; 0130 _squareTiles = shadow.tileSet( color, key ); 0131 0132 // re-install shadows for all windowId 0133 for( WidgetMap::const_iterator iter = _widgets.begin(); iter != _widgets.end(); ++iter ) 0134 { installX11Shadows( iter->first ); } 0135 0136 } 0137 0138 //______________________________________________ 0139 bool ShadowHelper::registerWidget( GtkWidget* widget ) 0140 { 0141 0142 // do nothing if not supported 0143 if( !_supported ) return false; 0144 0145 // check widget 0146 if( !( widget && GTK_IS_WINDOW( widget ) ) ) return false; 0147 0148 // make sure that widget is not already registered 0149 if( _widgets.find( widget ) != _widgets.end() ) return false; 0150 0151 // check if window is accepted 0152 if( !acceptWidget( widget ) ) return false; 0153 0154 // try install shadows 0155 installX11Shadows( widget ); 0156 0157 // register in map and returns success 0158 WidgetData data; 0159 data._destroyId.connect( G_OBJECT( widget ), "destroy", G_CALLBACK( destroyNotifyEvent ), this ); 0160 _widgets.insert( std::make_pair( widget, data ) ); 0161 0162 return true; 0163 0164 } 0165 0166 //______________________________________________ 0167 void ShadowHelper::unregisterWidget( GtkWidget* widget ) 0168 { 0169 // find matching data in map 0170 WidgetMap::iterator iter( _widgets.find( widget ) ); 0171 if( iter == _widgets.end() ) return; 0172 0173 // disconnect 0174 iter->second._destroyId.disconnect(); 0175 0176 // remove from map 0177 _widgets.erase( iter ); 0178 } 0179 0180 //______________________________________________ 0181 bool ShadowHelper::isMenu( GtkWidget* widget ) const 0182 { 0183 if( !( widget && GTK_IS_WINDOW( widget ) ) ) return false; 0184 const GdkWindowTypeHint hint( gtk_window_get_type_hint( GTK_WINDOW( widget ) ) ); 0185 return 0186 hint == GDK_WINDOW_TYPE_HINT_MENU || 0187 hint == GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU || 0188 hint == GDK_WINDOW_TYPE_HINT_POPUP_MENU; 0189 } 0190 0191 //______________________________________________ 0192 bool ShadowHelper::isToolTip( GtkWidget* widget ) const 0193 { 0194 if( !( widget && GTK_IS_WINDOW( widget ) ) ) return false; 0195 const GdkWindowTypeHint hint( gtk_window_get_type_hint( GTK_WINDOW( widget ) ) ); 0196 return hint == GDK_WINDOW_TYPE_HINT_TOOLTIP; 0197 } 0198 0199 //______________________________________________ 0200 bool ShadowHelper::acceptWidget( GtkWidget* widget ) const 0201 { 0202 0203 // check widget and type 0204 if( !( widget && GTK_IS_WINDOW( widget ) ) ) return false; 0205 0206 // for openoffice, accept all non decorated windows 0207 if( _applicationName.isOpenOffice() ) return true; 0208 0209 // otherwise check window hint 0210 const GdkWindowTypeHint hint( gtk_window_get_type_hint( GTK_WINDOW( widget ) ) ); 0211 return 0212 hint == GDK_WINDOW_TYPE_HINT_MENU || 0213 hint == GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU || 0214 hint == GDK_WINDOW_TYPE_HINT_POPUP_MENU || 0215 hint == GDK_WINDOW_TYPE_HINT_COMBO || 0216 hint == GDK_WINDOW_TYPE_HINT_TOOLTIP; 0217 } 0218 0219 //______________________________________________ 0220 void ShadowHelper::createPixmapHandles( void ) 0221 { 0222 0223 #if OXYGEN_DEBUG 0224 std::cerr << "Oxygen::ShadowHelper::createPixmapHandles" << std::endl; 0225 #endif 0226 0227 #ifdef GDK_WINDOWING_X11 0228 // create atom 0229 if( !_atom ) 0230 { 0231 0232 // get screen and check 0233 GdkScreen* screen = gdk_screen_get_default(); 0234 if( !screen ) 0235 { 0236 0237 #if OXYGEN_DEBUG 0238 std::cerr << "ShadowHelper::createPixmapHandles - screen is NULL" << std::endl; 0239 #endif 0240 0241 return; 0242 } 0243 0244 // get display and check 0245 Display* display( GDK_DISPLAY_XDISPLAY( gdk_screen_get_display( screen ) ) ); 0246 if( !display ) 0247 { 0248 0249 #if OXYGEN_DEBUG 0250 std::cerr << "ShadowHelper::createPixmapHandles - display is NULL" << std::endl; 0251 #endif 0252 0253 return; 0254 } 0255 0256 _atom = XInternAtom( display, netWMShadowAtomName, False); 0257 } 0258 0259 // make sure size is valid 0260 if( _size <= 0 ) return; 0261 0262 // opacity 0263 const int shadowOpacity = 150; 0264 0265 if( _roundPixmaps.empty() || _squarePixmaps.empty() ) 0266 { 0267 // get screen, display, visual and check 0268 // no need to check screen and display, since was already done for ATOM 0269 GdkScreen* screen = gdk_screen_get_default(); 0270 if( !gdk_screen_get_rgba_visual( screen ) ) 0271 { 0272 0273 #if OXYGEN_DEBUG 0274 std::cerr << "ShadowHelper::createPixmapHandles - no valid RGBA visual found." << std::endl; 0275 #endif 0276 0277 return; 0278 0279 } 0280 } 0281 0282 // make sure pixmaps are not already initialized 0283 if( _roundPixmaps.empty() ) 0284 { 0285 0286 _roundPixmaps.push_back( createPixmap( _roundTiles.surface( 1 ), shadowOpacity ) ); 0287 _roundPixmaps.push_back( createPixmap( _roundTiles.surface( 2 ), shadowOpacity ) ); 0288 _roundPixmaps.push_back( createPixmap( _roundTiles.surface( 5 ), shadowOpacity ) ); 0289 _roundPixmaps.push_back( createPixmap( _roundTiles.surface( 8 ), shadowOpacity ) ); 0290 _roundPixmaps.push_back( createPixmap( _roundTiles.surface( 7 ), shadowOpacity ) ); 0291 _roundPixmaps.push_back( createPixmap( _roundTiles.surface( 6 ), shadowOpacity ) ); 0292 _roundPixmaps.push_back( createPixmap( _roundTiles.surface( 3 ), shadowOpacity ) ); 0293 _roundPixmaps.push_back( createPixmap( _roundTiles.surface( 0 ), shadowOpacity ) ); 0294 0295 } 0296 0297 if( _squarePixmaps.empty() ) 0298 { 0299 0300 _squarePixmaps.push_back( createPixmap( _squareTiles.surface( 1 ), shadowOpacity ) ); 0301 _squarePixmaps.push_back( createPixmap( _squareTiles.surface( 2 ), shadowOpacity ) ); 0302 _squarePixmaps.push_back( createPixmap( _squareTiles.surface( 5 ), shadowOpacity ) ); 0303 _squarePixmaps.push_back( createPixmap( _squareTiles.surface( 8 ), shadowOpacity ) ); 0304 _squarePixmaps.push_back( createPixmap( _squareTiles.surface( 7 ), shadowOpacity ) ); 0305 _squarePixmaps.push_back( createPixmap( _squareTiles.surface( 6 ), shadowOpacity ) ); 0306 _squarePixmaps.push_back( createPixmap( _squareTiles.surface( 3 ), shadowOpacity ) ); 0307 _squarePixmaps.push_back( createPixmap( _squareTiles.surface( 0 ), shadowOpacity ) ); 0308 0309 } 0310 0311 #endif 0312 0313 } 0314 0315 //______________________________________________ 0316 #ifdef GDK_WINDOWING_X11 0317 Pixmap ShadowHelper::createPixmap( const Cairo::Surface& surface, int opacity ) const 0318 { 0319 assert( surface.isValid() ); 0320 int width(0); 0321 int height(0); 0322 cairo_surface_get_size( surface, width, height ); 0323 0324 GdkScreen* screen = gdk_screen_get_default(); 0325 Display* display( GDK_DISPLAY_XDISPLAY( gdk_screen_get_display( screen ) ) ); 0326 Window root( GDK_WINDOW_XID( gdk_screen_get_root_window( screen ) ) ); 0327 Pixmap pixmap = XCreatePixmap( display, root, width, height, 32 ); 0328 0329 // create surface for pixmap 0330 { 0331 Cairo::Surface dest( cairo_xlib_surface_create( display, pixmap, GDK_VISUAL_XVISUAL( gdk_screen_get_rgba_visual( screen ) ), width, height ) ); 0332 Cairo::Context context( dest ); 0333 cairo_set_operator( context, CAIRO_OPERATOR_SOURCE ); 0334 0335 cairo_rectangle( context, 0, 0, width, height ); 0336 cairo_set_source_surface( context, surface, 0, 0 ); 0337 cairo_fill( context ); 0338 0339 if( opacity < 255 ) 0340 { 0341 0342 cairo_set_operator( context, CAIRO_OPERATOR_DEST_IN ); 0343 cairo_set_source( context, ColorUtils::Rgba( 0, 0, 0, double(opacity)/255 ) ); 0344 cairo_rectangle( context, 0, 0, width, height ); 0345 cairo_fill( context ); 0346 0347 } 0348 0349 } 0350 0351 return pixmap; 0352 0353 } 0354 #endif 0355 0356 //______________________________________________ 0357 void ShadowHelper::installX11Shadows( GtkWidget* widget ) 0358 { 0359 0360 #ifdef GDK_WINDOWING_X11 0361 0362 #if OXYGEN_DEBUG 0363 std::cerr 0364 << "Oxygen::ShadowHelper::installX11Shadows - " 0365 << " widget: " << widget 0366 << " wid: " << GDK_WINDOW_XID( gtk_widget_get_window( widget ) ) 0367 << std::endl; 0368 #endif 0369 0370 // do nothing if not supported 0371 if( !_supported ) return; 0372 0373 // check widget 0374 if( !GTK_IS_WIDGET( widget ) ) return; 0375 0376 // make sure handles and atom are defined 0377 createPixmapHandles(); 0378 0379 GdkWindow *window = gtk_widget_get_window( widget ); 0380 GdkDisplay *display = gtk_widget_get_display( widget ); 0381 0382 std::vector<unsigned long> data; 0383 const bool isMenu( this->isMenu( widget ) ); 0384 const bool isToolTip( this->isToolTip( widget ) ); 0385 if( _applicationName.isOpenOffice() || ( (isMenu||isToolTip) && _applicationName.isXul( widget ) ) ) 0386 { 0387 0388 data = _squarePixmaps; 0389 data.push_back( _size ); 0390 data.push_back( _size ); 0391 data.push_back( _size ); 0392 data.push_back( _size ); 0393 0394 } else { 0395 0396 data = _roundPixmaps; 0397 if( isMenu ) 0398 { 0399 0400 /* 0401 for menus, need to shrink top and bottom shadow size, since body is done likely with respect to real size 0402 in painting method (Oxygen::Style::renderMenuBackground) 0403 */ 0404 data.push_back( _size - Menu_VerticalOffset ); 0405 data.push_back( _size ); 0406 data.push_back( _size - Menu_VerticalOffset ); 0407 data.push_back( _size ); 0408 0409 } else { 0410 0411 // all sides have same sizz 0412 data.push_back( _size ); 0413 data.push_back( _size ); 0414 data.push_back( _size ); 0415 data.push_back( _size ); 0416 0417 } 0418 0419 } 0420 0421 // change property 0422 XChangeProperty( 0423 GDK_DISPLAY_XDISPLAY( display ), GDK_WINDOW_XID(window), _atom, XA_CARDINAL, 32, PropModeReplace, 0424 reinterpret_cast<const unsigned char *>(&data[0]), data.size() ); 0425 0426 #endif 0427 0428 } 0429 0430 //_______________________________________________________ 0431 void ShadowHelper::uninstallX11Shadows( GtkWidget* widget ) const 0432 { 0433 0434 #ifdef GDK_WINDOWING_X11 0435 0436 // do nothing if not supported 0437 if( !_supported ) return; 0438 0439 if( !GTK_IS_WIDGET( widget ) ) return; 0440 GdkWindow *window = gtk_widget_get_window( widget ); 0441 GdkDisplay *display = gtk_widget_get_display( widget ); 0442 XDeleteProperty( GDK_DISPLAY_XDISPLAY( display ), GDK_WINDOW_XID(window), _atom); 0443 #endif 0444 0445 } 0446 0447 //_______________________________________________________ 0448 gboolean ShadowHelper::realizeHook( GSignalInvocationHint*, guint, const GValue* params, gpointer data ) 0449 { 0450 0451 // get widget from params 0452 GtkWidget* widget( GTK_WIDGET( g_value_get_object( params ) ) ); 0453 0454 // check type 0455 if( !GTK_IS_WIDGET( widget ) ) return FALSE; 0456 static_cast<ShadowHelper*>(data)->registerWidget( widget ); 0457 return TRUE; 0458 } 0459 0460 //____________________________________________________________________________________________ 0461 gboolean ShadowHelper::destroyNotifyEvent( GtkWidget* widget, gpointer data ) 0462 { 0463 static_cast<ShadowHelper*>(data)->unregisterWidget( widget ); 0464 return FALSE; 0465 } 0466 0467 }