File indexing completed on 2024-04-21 05:47:02
0001 /***************************************************************************** 0002 * Copyright 2003 - 2010 Craig Drummond <craig.p.drummond@gmail.com> * 0003 * Copyright 2013 - 2015 Yichao Yu <yyc1992@gmail.com> * 0004 * * 0005 * This program is free software; you can redistribute it and/or modify * 0006 * it under the terms of the GNU Lesser General Public License as * 0007 * published by the Free Software Foundation; either version 2.1 of the * 0008 * License, or (at your option) version 3, or any later version accepted * 0009 * by the membership of KDE e.V. (or its successor approved by the * 0010 * membership of KDE e.V.), which shall act as a proxy defined in * 0011 * Section 6 of version 3 of the license. * 0012 * * 0013 * This program is distributed in the hope that it will be useful, * 0014 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * 0016 * Lesser General Public License for more details. * 0017 * * 0018 * You should have received a copy of the GNU Lesser General Public * 0019 * License along with this library. If not, * 0020 * see <http://www.gnu.org/licenses/>. * 0021 *****************************************************************************/ 0022 0023 #include "window.h" 0024 0025 #include <qtcurve-utils/x11qtc.h> 0026 #include <qtcurve-utils/x11wrap.h> 0027 #include <qtcurve-utils/gtkprops.h> 0028 #include <qtcurve-utils/log.h> 0029 #include <qtcurve-cairo/utils.h> 0030 0031 #include <gdk/gdkkeysyms.h> 0032 #include <gdk/gdkx.h> 0033 #include <common/common.h> 0034 #include <common/config_file.h> 0035 #include "qt_settings.h" 0036 #include "menu.h" 0037 #include "dbus.h" 0038 0039 namespace QtCurve { 0040 namespace Window { 0041 0042 static GtkWidget *currentActiveWindow = nullptr; 0043 0044 typedef struct { 0045 int width; 0046 int height; 0047 int timer; 0048 GtkWidget *widget; 0049 bool locked; 0050 } QtCWindow; 0051 0052 static GHashTable *table = nullptr; 0053 0054 static QtCWindow* 0055 lookupHash(void *hash, bool create) 0056 { 0057 QtCWindow *rv = nullptr; 0058 0059 if (!table) 0060 table = g_hash_table_new(g_direct_hash, g_direct_equal); 0061 0062 rv = (QtCWindow*)g_hash_table_lookup(table, hash); 0063 0064 if (!rv && create) { 0065 rv = qtcNew(QtCWindow); 0066 rv->width = rv->height = rv->timer = 0; 0067 rv->widget = nullptr; 0068 rv->locked = false; 0069 g_hash_table_insert(table, hash, rv); 0070 rv = (QtCWindow*)g_hash_table_lookup(table, hash); 0071 } 0072 return rv; 0073 } 0074 0075 static void 0076 removeFromHash(void *hash) 0077 { 0078 if (table) { 0079 QtCWindow *tv = lookupHash(hash, false); 0080 if (tv) { 0081 if (tv->timer) { 0082 g_source_remove(tv->timer); 0083 g_object_unref(G_OBJECT(tv->widget)); 0084 } 0085 g_hash_table_remove(table, hash); 0086 } 0087 } 0088 } 0089 0090 static void 0091 cleanup(GtkWidget *widget) 0092 { 0093 if (widget) { 0094 GtkWidgetProps props(widget); 0095 if (!(qtcIsFlatBgnd(opts.bgndAppearance)) || 0096 opts.bgndImage.type != IMG_NONE) { 0097 removeFromHash(widget); 0098 props->windowConfigure.disconn(); 0099 } 0100 props->windowDestroy.disconn(); 0101 props->windowStyleSet.disconn(); 0102 if ((opts.menubarHiding & HIDE_KEYBOARD) || 0103 (opts.statusbarHiding & HIDE_KEYBOARD)) 0104 props->windowKeyRelease.disconn(); 0105 if ((opts.menubarHiding & HIDE_KWIN) || 0106 (opts.statusbarHiding & HIDE_KWIN)) 0107 props->windowMap.disconn(); 0108 if (opts.shadeMenubarOnlyWhenActive || BLEND_TITLEBAR || 0109 opts.menubarHiding || opts.statusbarHiding) 0110 props->windowClientEvent.disconn(); 0111 props->windowHacked = false; 0112 } 0113 } 0114 0115 static gboolean 0116 styleSet(GtkWidget *widget, GtkStyle*, void*) 0117 { 0118 cleanup(widget); 0119 return false; 0120 } 0121 0122 static bool toggleMenuBar(GtkWidget *widget); 0123 static bool toggleStatusBar(GtkWidget *widget); 0124 0125 static gboolean 0126 clientEvent(GtkWidget *widget, GdkEventClient *event, void*) 0127 { 0128 if (gdk_x11_atom_to_xatom(event->message_type) == 0129 qtc_x11_qtc_active_window) { 0130 if (event->data.l[0]) { 0131 currentActiveWindow = widget; 0132 } else if (currentActiveWindow == widget) { 0133 currentActiveWindow = nullptr; 0134 } 0135 gtk_widget_queue_draw(widget); 0136 } else if (gdk_x11_atom_to_xatom(event->message_type) == 0137 qtc_x11_qtc_titlebar_size) { 0138 qtcGetWindowBorderSize(true); 0139 GtkWidget *menubar = getMenuBar(widget, 0); 0140 0141 if (menubar) { 0142 gtk_widget_queue_draw(menubar); 0143 } 0144 } else if (gdk_x11_atom_to_xatom(event->message_type) == 0145 qtc_x11_qtc_toggle_menubar) { 0146 if (opts.menubarHiding & HIDE_KWIN && toggleMenuBar(widget)) { 0147 gtk_widget_queue_draw(widget); 0148 } 0149 } else if (gdk_x11_atom_to_xatom(event->message_type) == 0150 qtc_x11_qtc_toggle_statusbar) { 0151 if (opts.statusbarHiding & HIDE_KWIN && 0152 toggleStatusBar(widget)) { 0153 gtk_widget_queue_draw(widget); 0154 } 0155 } 0156 return false; 0157 } 0158 0159 static gboolean 0160 destroy(GtkWidget *widget, GdkEvent*, void*) 0161 { 0162 cleanup(widget); 0163 return false; 0164 } 0165 0166 static bool 0167 sizeRequest(GtkWidget *widget) 0168 { 0169 if (widget && (!(qtcIsFlatBgnd(opts.bgndAppearance)) || 0170 IMG_NONE != opts.bgndImage.type)) { 0171 QtcRect alloc = Widget::getAllocation(widget); 0172 QtcRect rect = {0, 0, 0, 0}; 0173 if (qtcIsFlat(opts.bgndAppearance) && 0174 IMG_NONE != opts.bgndImage.type) { 0175 EPixPos pos = (IMG_FILE == opts.bgndImage.type ? 0176 opts.bgndImage.pos : PP_TR); 0177 if (opts.bgndImage.type == IMG_FILE) { 0178 qtcLoadBgndImage(&opts.bgndImage); 0179 } 0180 switch (pos) { 0181 case PP_TL: 0182 rect.width = opts.bgndImage.width + 1; 0183 rect.height = opts.bgndImage.height + 1; 0184 break; 0185 case PP_TM: 0186 case PP_TR: 0187 rect.width = alloc.width; 0188 rect.height = (opts.bgndImage.type == IMG_FILE ? 0189 opts.bgndImage.height : 0190 RINGS_HEIGHT(opts.bgndImage.type)) + 1; 0191 break; 0192 case PP_LM: 0193 case PP_BL: 0194 rect.width = opts.bgndImage.width + 1; 0195 rect.height = alloc.height; 0196 break; 0197 case PP_CENTRED: 0198 case PP_BR: 0199 case PP_BM: 0200 case PP_RM: 0201 rect.width = alloc.width; 0202 rect.height = alloc.height; 0203 break; 0204 } 0205 if (alloc.width < rect.width) { 0206 rect.width = alloc.width; 0207 } 0208 if (alloc.height < rect.height) { 0209 rect.height = alloc.height; 0210 } 0211 } else { 0212 rect.width = alloc.width, rect.height = alloc.height; 0213 } 0214 gdk_window_invalidate_rect(gtk_widget_get_window(widget), 0215 (GdkRectangle*)&rect, false); 0216 } 0217 return false; 0218 } 0219 0220 static gboolean 0221 delayedUpdate(void *user_data) 0222 { 0223 QtCWindow *window = (QtCWindow*)user_data; 0224 0225 if (window) { 0226 if (window->locked) { 0227 window->locked = false; 0228 return true; 0229 } else { 0230 g_source_remove(window->timer); 0231 window->timer = 0; 0232 // otherwise, trigger update 0233 gdk_threads_enter(); 0234 sizeRequest(window->widget); 0235 gdk_threads_leave(); 0236 g_object_unref(G_OBJECT(window->widget)); 0237 return false; 0238 } 0239 } 0240 return false; 0241 } 0242 0243 static gboolean 0244 configure(GtkWidget*, GdkEventConfigure *event, void *data) 0245 { 0246 QtCWindow *window = (QtCWindow*)data; 0247 0248 if (window && (event->width != window->width || 0249 event->height != window->height)) { 0250 window->width = event->width; 0251 window->height = event->height; 0252 0253 // schedule delayed timeOut 0254 if (!window->timer) { 0255 g_object_ref(G_OBJECT(window->widget)); 0256 window->timer = 0257 g_timeout_add(50, delayedUpdate, window); 0258 window->locked = false; 0259 } else { 0260 window->locked = true; 0261 } 0262 } 0263 return false; 0264 } 0265 0266 static bool 0267 canGetChildren(GtkWidget *widget) 0268 { 0269 return (qtSettings.app != GTK_APP_GHB || 0270 noneOf(gTypeName(widget), "GhbCompositor") || 0271 gtk_widget_get_realized(widget)); 0272 } 0273 0274 static bool 0275 toggleMenuBar(GtkWidget *widget) 0276 { 0277 GtkWidget *menuBar = getMenuBar(widget, 0); 0278 0279 if (menuBar) { 0280 int size = 0; 0281 qtcSetMenuBarHidden(qtSettings.appName, 0282 gtk_widget_get_visible(menuBar)); 0283 if (gtk_widget_get_visible(menuBar)) { 0284 gtk_widget_hide(menuBar); 0285 } else { 0286 size = Widget::getAllocation(menuBar).height; 0287 gtk_widget_show(menuBar); 0288 } 0289 0290 Menu::emitSize(menuBar, size); 0291 menuBarDBus(widget, size); 0292 return true; 0293 } 0294 return false; 0295 } 0296 0297 static bool 0298 toggleStatusBar(GtkWidget *widget) 0299 { 0300 GtkWidget *statusBar = getStatusBar(widget, 0); 0301 0302 if (statusBar) { 0303 bool state = gtk_widget_get_visible(statusBar); 0304 qtcSetStatusBarHidden(qtSettings.appName, state); 0305 if (state) { 0306 gtk_widget_hide(statusBar); 0307 } else { 0308 gtk_widget_show(statusBar); 0309 } 0310 statusBarDBus(widget, state); 0311 return true; 0312 } 0313 return false; 0314 } 0315 0316 static void 0317 setProperties(GtkWidget *w, unsigned short opacity) 0318 { 0319 GtkWindow *topLevel = GTK_WINDOW(gtk_widget_get_toplevel(w)); 0320 unsigned long prop = (qtcIsFlatBgnd(opts.bgndAppearance) ? 0321 (IMG_NONE != opts.bgndImage.type ? 0322 APPEARANCE_RAISED : APPEARANCE_FLAT) : 0323 opts.bgndAppearance) & 0xFF; 0324 //GtkRcStyle *rcStyle=gtk_widget_get_modifier_style(w); 0325 GdkColor *bgnd = /* rcStyle ? &rcStyle->bg[GTK_STATE_NORMAL] : */ 0326 &qtcPalette.background[ORIGINAL_SHADE]; 0327 xcb_window_t wid = 0328 GDK_WINDOW_XID(gtk_widget_get_window(GTK_WIDGET(topLevel))); 0329 0330 if (opacity != 100) { 0331 qtcX11SetOpacity(wid, opacity); 0332 } 0333 prop |= (((toQtColor(bgnd->red) & 0xFF) << 24) | 0334 ((toQtColor(bgnd->green) & 0xFF) << 16) | 0335 ((toQtColor(bgnd->blue) & 0xFF) << 8)); 0336 qtcX11ChangeProperty(XCB_PROP_MODE_REPLACE, wid, qtc_x11_qtc_bgnd, 0337 XCB_ATOM_CARDINAL, 32, 1, &prop); 0338 qtcX11Flush(); 0339 } 0340 0341 static gboolean 0342 keyRelease(GtkWidget *widget, GdkEventKey *event, void*) 0343 { 0344 // Ensure only ctrl/alt/shift/capsLock are pressed... 0345 if (GDK_CONTROL_MASK & event->state && GDK_MOD1_MASK & event->state && 0346 !event->is_modifier && 0 == (event->state & 0xFF00)) { 0347 bool toggled = false; 0348 if (opts.menubarHiding & HIDE_KEYBOARD && 0349 (GDK_KEY_m == event->keyval || GDK_KEY_M == event->keyval)) { 0350 toggled = toggleMenuBar(widget); 0351 } 0352 if (opts.statusbarHiding & HIDE_KEYBOARD && 0353 (GDK_KEY_s == event->keyval || GDK_KEY_S == event->keyval)) { 0354 toggled = toggleStatusBar(widget); 0355 } 0356 if (toggled) { 0357 gtk_widget_queue_draw(widget); 0358 } 0359 } 0360 return false; 0361 } 0362 0363 static gboolean 0364 mapWindow(GtkWidget *widget, GdkEventKey*, void*) 0365 { 0366 GtkWidgetProps props(widget); 0367 setProperties(widget, props->windowOpacity); 0368 0369 if (opts.menubarHiding & HIDE_KWIN) { 0370 GtkWidget *menuBar = getMenuBar(widget, 0); 0371 0372 if (menuBar) { 0373 int size = (gtk_widget_get_visible(menuBar) ? 0374 Widget::getAllocation(menuBar).height : 0); 0375 0376 Menu::emitSize(menuBar, size); 0377 menuBarDBus(widget, size); 0378 } 0379 } 0380 0381 if (opts.statusbarHiding & HIDE_KWIN) { 0382 GtkWidget *statusBar = getStatusBar(widget, 0); 0383 0384 if (statusBar) { 0385 statusBarDBus(widget, !gtk_widget_get_visible(statusBar)); 0386 } 0387 } 0388 return false; 0389 } 0390 0391 bool 0392 isActive(GtkWidget *widget) 0393 { 0394 return widget && (gtk_window_is_active(GTK_WINDOW(widget)) || 0395 currentActiveWindow == widget); 0396 } 0397 0398 bool 0399 setup(GtkWidget *widget, int opacity) 0400 { 0401 GtkWidgetProps props(widget); 0402 if (widget && !props->windowHacked) { 0403 props->windowHacked = true; 0404 if (!qtcIsFlatBgnd(opts.bgndAppearance) || 0405 opts.bgndImage.type != IMG_NONE) { 0406 QtCWindow *window = lookupHash(widget, true); 0407 if (window) { 0408 QtcRect alloc = Widget::getAllocation(widget); 0409 props->windowConfigure.conn("configure-event", 0410 configure, window); 0411 window->width = alloc.width; 0412 window->height = alloc.height; 0413 window->widget = widget; 0414 } 0415 } 0416 props->windowDestroy.conn("destroy-event", destroy); 0417 props->windowStyleSet.conn("style-set", styleSet); 0418 if ((opts.menubarHiding & HIDE_KEYBOARD) || 0419 (opts.statusbarHiding & HIDE_KEYBOARD)) { 0420 props->windowKeyRelease.conn("key-release-event", keyRelease); 0421 } 0422 props->windowOpacity = (unsigned short)opacity; 0423 setProperties(widget, (unsigned short)opacity); 0424 0425 if ((opts.menubarHiding & HIDE_KWIN) || 0426 (opts.statusbarHiding & HIDE_KWIN) || 100 != opacity) 0427 props->windowMap.conn("map-event", mapWindow); 0428 if (opts.shadeMenubarOnlyWhenActive || BLEND_TITLEBAR || 0429 opts.menubarHiding || opts.statusbarHiding) 0430 props->windowClientEvent.conn("client-event", clientEvent); 0431 return true; 0432 } 0433 return false; 0434 } 0435 0436 GtkWidget* 0437 getMenuBar(GtkWidget *parent, int level) 0438 { 0439 if (level < 3 && GTK_IS_CONTAINER(parent) && canGetChildren(parent) 0440 /* && gtk_widget_get_realized(parent)*/) { 0441 GtkWidget *rv = nullptr; 0442 GList *children = gtk_container_get_children(GTK_CONTAINER(parent)); 0443 for (GList *child = children;child && !rv;child = child->next) { 0444 GtkWidget *boxChild = (GtkWidget*)child->data; 0445 0446 if (GTK_IS_MENU_BAR(boxChild)) { 0447 rv = GTK_WIDGET(boxChild); 0448 } else if (GTK_IS_CONTAINER(boxChild)) { 0449 rv = getMenuBar(GTK_WIDGET(boxChild), level + 1); 0450 } 0451 } 0452 0453 if (children) { 0454 g_list_free(children); 0455 } 0456 return rv; 0457 } 0458 return nullptr; 0459 } 0460 0461 bool 0462 setStatusBarProp(GtkWidget *w) 0463 { 0464 GtkWidgetProps props(w); 0465 if (w && !props->statusBarSet) { 0466 GtkWindow *topLevel = GTK_WINDOW(gtk_widget_get_toplevel(w)); 0467 xcb_window_t wid = 0468 GDK_WINDOW_XID(gtk_widget_get_window(GTK_WIDGET(topLevel))); 0469 0470 props->statusBarSet = true; 0471 qtcX11SetStatusBar(wid); 0472 return true; 0473 } 0474 return false; 0475 } 0476 0477 GtkWidget* 0478 getStatusBar(GtkWidget *parent, int level) 0479 { 0480 if (level < 3 && GTK_IS_CONTAINER(parent) && canGetChildren(parent) 0481 /* && gtk_widget_get_realized(parent)*/) { 0482 GtkWidget *rv = nullptr; 0483 GList *children = gtk_container_get_children(GTK_CONTAINER(parent)); 0484 for(GList *child = children;child && !rv;child = child->next) { 0485 GtkWidget *boxChild = (GtkWidget*)child->data; 0486 0487 if (GTK_IS_STATUSBAR(boxChild)) { 0488 rv=GTK_WIDGET(boxChild); 0489 } else if (GTK_IS_CONTAINER(boxChild)) { 0490 rv = getStatusBar(GTK_WIDGET(boxChild), level + 1); 0491 } 0492 } 0493 if (children) { 0494 g_list_free(children); 0495 } 0496 return rv; 0497 } 0498 return nullptr; 0499 } 0500 0501 void 0502 statusBarDBus(GtkWidget *widget, bool state) 0503 { 0504 GtkWindow *topLevel = GTK_WINDOW(gtk_widget_get_toplevel(widget)); 0505 uint32_t xid = GDK_WINDOW_XID(gtk_widget_get_window(GTK_WIDGET(topLevel))); 0506 GDBus::callMethod("org.kde.kwin", "/QtCurve", "org.kde.QtCurve", 0507 "statusBarState", xid, state); 0508 } 0509 0510 void 0511 menuBarDBus(GtkWidget *widget, int32_t size) 0512 { 0513 GtkWindow *topLevel = GTK_WINDOW(gtk_widget_get_toplevel(widget)); 0514 uint32_t xid = GDK_WINDOW_XID(gtk_widget_get_window(GTK_WIDGET(topLevel))); 0515 GDBus::callMethod("org.kde.kwin", "/QtCurve", "org.kde.QtCurve", 0516 "menuBarSize", xid, size); 0517 } 0518 0519 } 0520 }