1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
25  */
26 
27 #include <string.h>
28 #include "gtkbgbox.h"
29 #include "bg.h"
30 #include <gdk/gdk.h>
31 #include <gdk/gdkx.h>
32 #include <gdk/gdkpixmap.h>
33 #include <gdk/gdkprivate.h>
34 #include <glib.h>
35 #include <glib-object.h>
36 
37 
38 //#define DEBUGPRN
39 #include "dbg.h"
40 
41 typedef struct {
42     GdkPixmap *pixmap;
43     guint32 tintcolor;
44     gint alpha;
45     int bg_type;
46     FbBg *bg;
47     gulong sid;
48 } GtkBgboxPrivate;
49 
50 
51 
52 #define GTK_BGBOX_GET_PRIVATE(obj)  G_TYPE_INSTANCE_GET_PRIVATE((obj), GTK_TYPE_BGBOX, GtkBgboxPrivate)
53 
54 static void gtk_bgbox_class_init    (GtkBgboxClass *klass);
55 static void gtk_bgbox_init          (GtkBgbox *bgbox);
56 static void gtk_bgbox_realize       (GtkWidget *widget);
57 static void gtk_bgbox_size_request  (GtkWidget *widget, GtkRequisition   *requisition);
58 static void gtk_bgbox_size_allocate (GtkWidget *widget, GtkAllocation    *allocation);
59 static void gtk_bgbox_style_set (GtkWidget *widget, GtkStyle  *previous_style);
60 static gboolean gtk_bgbox_configure_event(GtkWidget *widget, GdkEventConfigure *e);
61 #if 0
62 static gboolean gtk_bgbox_destroy_event (GtkWidget *widget, GdkEventAny *event);
63 static gboolean gtk_bgbox_delete_event (GtkWidget *widget, GdkEventAny *event);
64 #endif
65 
66 static void gtk_bgbox_finalize (GObject *object);
67 
68 static void gtk_bgbox_set_bg_root(GtkWidget *widget, GtkBgboxPrivate *priv);
69 static void gtk_bgbox_set_bg_inherit(GtkWidget *widget, GtkBgboxPrivate *priv);
70 static void gtk_bgbox_bg_changed(FbBg *bg, GtkWidget *widget);
71 
72 static GtkBinClass *parent_class = NULL;
73 
74 GType
gtk_bgbox_get_type(void)75 gtk_bgbox_get_type (void)
76 {
77     static GType bgbox_type = 0;
78 
79     if (!bgbox_type)
80     {
81         static const GTypeInfo bgbox_info =
82             {
83                 sizeof (GtkBgboxClass),
84                 NULL,		/* base_init */
85                 NULL,		/* base_finalize */
86                 (GClassInitFunc) gtk_bgbox_class_init,
87                 NULL,		/* class_finalize */
88                 NULL,		/* class_data */
89                 sizeof (GtkBgbox),
90                 0,		/* n_preallocs */
91                 (GInstanceInitFunc) gtk_bgbox_init,
92             };
93 
94         bgbox_type = g_type_register_static (GTK_TYPE_BIN, "GtkBgbox",
95               &bgbox_info, 0);
96     }
97 
98     return bgbox_type;
99 }
100 
101 static void
gtk_bgbox_class_init(GtkBgboxClass * class)102 gtk_bgbox_class_init (GtkBgboxClass *class)
103 {
104     GObjectClass *object_class = G_OBJECT_CLASS (class);
105     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
106 
107     parent_class = g_type_class_peek_parent (class);
108 
109     widget_class->realize         = gtk_bgbox_realize;
110     widget_class->size_request    = gtk_bgbox_size_request;
111     widget_class->size_allocate   = gtk_bgbox_size_allocate;
112     widget_class->style_set       = gtk_bgbox_style_set;
113     widget_class->configure_event = gtk_bgbox_configure_event;
114     //widget_class->destroy_event   = gtk_bgbox_destroy_event;
115     //widget_class->delete_event    = gtk_bgbox_delete_event;
116 
117     object_class->finalize = gtk_bgbox_finalize;
118     g_type_class_add_private (class, sizeof (GtkBgboxPrivate));
119 }
120 
121 static void
gtk_bgbox_init(GtkBgbox * bgbox)122 gtk_bgbox_init (GtkBgbox *bgbox)
123 {
124     GtkBgboxPrivate *priv;
125 
126     ENTER;
127     GTK_WIDGET_UNSET_FLAGS (bgbox, GTK_NO_WINDOW);
128 
129     priv = GTK_BGBOX_GET_PRIVATE (bgbox);
130     priv->bg_type = BG_NONE;
131     priv->sid = 0;
132     RET();
133 }
134 
135 GtkWidget*
gtk_bgbox_new(void)136 gtk_bgbox_new (void)
137 {
138     ENTER;
139     RET(g_object_new (GTK_TYPE_BGBOX, NULL));
140 }
141 
142 static void
gtk_bgbox_finalize(GObject * object)143 gtk_bgbox_finalize (GObject *object)
144 {
145     GtkBgboxPrivate *priv;
146 
147     ENTER;
148     priv = GTK_BGBOX_GET_PRIVATE(GTK_WIDGET(object));
149     if (priv->pixmap) {
150         g_object_unref(priv->pixmap);
151         priv->pixmap = NULL;
152     }
153     if (priv->sid) {
154         g_signal_handler_disconnect(priv->bg, priv->sid);
155         priv->sid = 0;
156     }
157     if (priv->bg) {
158         g_object_unref(priv->bg);
159         priv->bg = NULL;
160     }
161     RET();
162 }
163 
164 static GdkFilterReturn
gtk_bgbox_event_filter(GdkXEvent * xevent,GdkEvent * event,GtkWidget * widget)165 gtk_bgbox_event_filter(GdkXEvent *xevent, GdkEvent *event, GtkWidget *widget)
166 {
167     XEvent *ev = (XEvent *) xevent;
168 
169     ENTER;
170     if (ev->type == ConfigureNotify) {
171         gtk_widget_queue_draw(widget);
172         //gtk_bgbox_style_set(widget, NULL);
173         DBG("ConfigureNotify %d %d %d %d\n",
174               ev->xconfigure.x,
175               ev->xconfigure.y,
176               ev->xconfigure.width,
177               ev->xconfigure.height
178             );
179     }
180     RET(GDK_FILTER_CONTINUE);
181 }
182 
183 static void
gtk_bgbox_realize(GtkWidget * widget)184 gtk_bgbox_realize (GtkWidget *widget)
185 {
186     GdkWindowAttr attributes;
187     gint attributes_mask;
188     gint border_width;
189     GtkBgboxPrivate *priv;
190 
191     ENTER;
192     GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
193 
194     border_width = GTK_CONTAINER (widget)->border_width;
195 
196     attributes.x = widget->allocation.x + border_width;
197     attributes.y = widget->allocation.y + border_width;
198     attributes.width = widget->allocation.width - 2*border_width;
199     attributes.height = widget->allocation.height - 2*border_width;
200     attributes.window_type = GDK_WINDOW_CHILD;
201     attributes.event_mask = gtk_widget_get_events (widget)
202         | GDK_BUTTON_MOTION_MASK
203         | GDK_BUTTON_PRESS_MASK
204         | GDK_BUTTON_RELEASE_MASK
205         | GDK_ENTER_NOTIFY_MASK
206         | GDK_LEAVE_NOTIFY_MASK
207         | GDK_EXPOSURE_MASK
208         | GDK_STRUCTURE_MASK;
209 
210     priv = GTK_BGBOX_GET_PRIVATE (widget);
211 
212     attributes.visual = gtk_widget_get_visual (widget);
213     attributes.colormap = gtk_widget_get_colormap (widget);
214     attributes.wclass = GDK_INPUT_OUTPUT;
215 
216     attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
217 
218     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
219           &attributes, attributes_mask);
220     gdk_window_set_user_data (widget->window, widget);
221     widget->style = gtk_style_attach (widget->style, widget->window);
222     if (priv->bg_type == BG_NONE)
223         gtk_bgbox_set_background(widget, BG_STYLE, 0, 0);
224     gdk_window_add_filter(widget->window,  (GdkFilterFunc) gtk_bgbox_event_filter, widget);
225     RET();
226 }
227 
228 
229 static void
gtk_bgbox_style_set(GtkWidget * widget,GtkStyle * previous_style)230 gtk_bgbox_style_set (GtkWidget *widget, GtkStyle  *previous_style)
231 {
232     GtkBgboxPrivate *priv;
233 
234     ENTER;
235     priv = GTK_BGBOX_GET_PRIVATE (widget);
236     if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_NO_WINDOW (widget)) {
237         gtk_bgbox_set_background(widget, priv->bg_type, priv->tintcolor, priv->alpha);
238     }
239     RET();
240 }
241 
242 /* gtk discards configure_event for GTK_WINDOW_CHILD. too pitty */
243 static  gboolean
gtk_bgbox_configure_event(GtkWidget * widget,GdkEventConfigure * e)244 gtk_bgbox_configure_event (GtkWidget *widget, GdkEventConfigure *e)
245 {
246     ENTER;
247     DBG("geom: size (%d, %d). pos (%d, %d)\n", e->width, e->height, e->x, e->y);
248     RET(FALSE);
249 
250 }
251 
252 static void
gtk_bgbox_size_request(GtkWidget * widget,GtkRequisition * requisition)253 gtk_bgbox_size_request (GtkWidget *widget, GtkRequisition *requisition)
254 {
255     GtkBin *bin = GTK_BIN (widget);
256     ENTER;
257     requisition->width = GTK_CONTAINER (widget)->border_width * 2;
258     requisition->height = GTK_CONTAINER (widget)->border_width * 2;
259 
260     if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
261     {
262         GtkRequisition child_requisition;
263 
264         gtk_widget_size_request (bin->child, &child_requisition);
265 
266         requisition->width += child_requisition.width;
267         requisition->height += child_requisition.height;
268     }
269     RET();
270 }
271 
272 /* calls with same allocation are usually refer to exactly same background
273  * and we just skip them for optimization reason.
274  * so if you see artifacts or unupdated background - reallocate bg on every call
275  */
276 static void
gtk_bgbox_size_allocate(GtkWidget * widget,GtkAllocation * wa)277 gtk_bgbox_size_allocate (GtkWidget *widget, GtkAllocation *wa)
278 {
279     GtkBin *bin;
280     GtkAllocation ca;
281     GtkBgboxPrivate *priv;
282     int same_alloc, border;
283 
284     ENTER;
285     same_alloc = !memcmp(&widget->allocation, wa, sizeof(*wa));
286     DBG("same alloc = %d\n", same_alloc);
287     DBG("x=%d y=%d w=%d h=%d\n", wa->x, wa->y, wa->width, wa->height);
288     DBG("x=%d y=%d w=%d h=%d\n", widget->allocation.x, widget->allocation.y,
289           widget->allocation.width, widget->allocation.height);
290     widget->allocation = *wa;
291     bin = GTK_BIN (widget);
292     border = GTK_CONTAINER (widget)->border_width;
293     ca.x = border;
294     ca.y = border;
295     ca.width  = MAX (wa->width  - border * 2, 0);
296     ca.height = MAX (wa->height - border * 2, 0);
297 
298     if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_NO_WINDOW (widget)
299           && !same_alloc) {
300         priv = GTK_BGBOX_GET_PRIVATE (widget);
301         DBG("move resize pos=%d,%d geom=%dx%d\n", wa->x, wa->y, wa->width,
302                 wa->height);
303         gdk_window_move_resize (widget->window, wa->x, wa->y, wa->width, wa->height);
304         gtk_bgbox_set_background(widget, priv->bg_type, priv->tintcolor, priv->alpha);
305     }
306 
307     if (bin->child)
308         gtk_widget_size_allocate (bin->child, &ca);
309     RET();
310 }
311 
312 
313 static void
gtk_bgbox_bg_changed(FbBg * bg,GtkWidget * widget)314 gtk_bgbox_bg_changed(FbBg *bg, GtkWidget *widget)
315 {
316     GtkBgboxPrivate *priv;
317 
318     ENTER;
319     priv = GTK_BGBOX_GET_PRIVATE (widget);
320     if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_NO_WINDOW (widget)) {
321         gtk_bgbox_set_background(widget, priv->bg_type, priv->tintcolor, priv->alpha);
322     }
323     RET();
324 }
325 
326 void
gtk_bgbox_set_background(GtkWidget * widget,int bg_type,guint32 tintcolor,gint alpha)327 gtk_bgbox_set_background(GtkWidget *widget, int bg_type, guint32 tintcolor, gint alpha)
328 {
329     GtkBgboxPrivate *priv;
330 
331     ENTER;
332     if (!(GTK_IS_BGBOX (widget)))
333         RET();
334 
335     priv = GTK_BGBOX_GET_PRIVATE (widget);
336     DBG("widget=%p bg_type old:%d new:%d\n", widget, priv->bg_type, bg_type);
337     if (priv->pixmap) {
338         g_object_unref(priv->pixmap);
339         priv->pixmap = NULL;
340     }
341     priv->bg_type = bg_type;
342     if (priv->bg_type == BG_STYLE) {
343         gtk_style_set_background(widget->style, widget->window, widget->state);
344         if (priv->sid) {
345             g_signal_handler_disconnect(priv->bg, priv->sid);
346             priv->sid = 0;
347         }
348         if (priv->bg) {
349             g_object_unref(priv->bg);
350             priv->bg = NULL;
351         }
352     } else {
353         if (!priv->bg)
354             priv->bg = fb_bg_get_for_display();
355         if (!priv->sid)
356             priv->sid = g_signal_connect(G_OBJECT(priv->bg), "changed", G_CALLBACK(gtk_bgbox_bg_changed), widget);
357 
358         if (priv->bg_type == BG_ROOT) {
359             priv->tintcolor = tintcolor;
360             priv->alpha = alpha;
361             gtk_bgbox_set_bg_root(widget, priv);
362         } else if (priv->bg_type == BG_INHERIT) {
363             gtk_bgbox_set_bg_inherit(widget, priv);
364         }
365     }
366     gtk_widget_queue_draw(widget);
367     g_object_notify(G_OBJECT (widget), "style");
368 
369     DBG("queue draw all %p\n", widget);
370     RET();
371 }
372 
373 static void
gtk_bgbox_set_bg_root(GtkWidget * widget,GtkBgboxPrivate * priv)374 gtk_bgbox_set_bg_root(GtkWidget *widget, GtkBgboxPrivate *priv)
375 {
376     priv = GTK_BGBOX_GET_PRIVATE (widget);
377 
378     ENTER;
379     priv->pixmap = fb_bg_get_xroot_pix_for_win(priv->bg, widget);
380     if (!priv->pixmap || priv->pixmap ==  GDK_NO_BG) {
381         //priv->bg_type = BG_NONE;
382         priv->pixmap = NULL;
383         gtk_style_set_background(widget->style, widget->window, widget->state);
384         gtk_widget_queue_draw_area(widget, 0, 0,
385               widget->allocation.width, widget->allocation.height);
386         DBG("no root pixmap was found\n");
387         RET();
388     }
389     if (priv->alpha)
390         fb_bg_composite(priv->pixmap, widget->style->black_gc,
391               priv->tintcolor, priv->alpha);
392     gdk_window_set_back_pixmap(widget->window, priv->pixmap, FALSE);
393     RET();
394 }
395 
396 static void
gtk_bgbox_set_bg_inherit(GtkWidget * widget,GtkBgboxPrivate * priv)397 gtk_bgbox_set_bg_inherit(GtkWidget *widget, GtkBgboxPrivate *priv)
398 {
399     priv = GTK_BGBOX_GET_PRIVATE (widget);
400 
401     ENTER;
402     gdk_window_set_back_pixmap(widget->window, NULL, TRUE);
403     RET();
404 }
405