From f9b59a49111b8db151af72aae334887105b4141b Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Wed, 6 Feb 2008 19:40:21 -0500 Subject: make the internal window type more easily inheritable --- openbox/focus_cycle_indicator.c | 72 ++++++++++++++++++++--------------------- openbox/window.c | 2 +- openbox/window.h | 4 +-- 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/openbox/focus_cycle_indicator.c b/openbox/focus_cycle_indicator.c index 7c5d5290..79de673b 100644 --- a/openbox/focus_cycle_indicator.c +++ b/openbox/focus_cycle_indicator.c @@ -59,23 +59,23 @@ void focus_cycle_indicator_startup(gboolean reconfig) if (reconfig) return; - focus_indicator.top.obwin.type = Window_Internal; - focus_indicator.left.obwin.type = Window_Internal; - focus_indicator.right.obwin.type = Window_Internal; - focus_indicator.bottom.obwin.type = Window_Internal; + focus_indicator.top.type = Window_Internal; + focus_indicator.left.type = Window_Internal; + focus_indicator.right.type = Window_Internal; + focus_indicator.bottom.type = Window_Internal; attr.override_redirect = True; attr.background_pixel = BlackPixel(ob_display, ob_screen); - focus_indicator.top.win = + focus_indicator.top.window = create_window(RootWindow(ob_display, ob_screen), CWOverrideRedirect | CWBackPixel, &attr); - focus_indicator.left.win = + focus_indicator.left.window = create_window(RootWindow(ob_display, ob_screen), CWOverrideRedirect | CWBackPixel, &attr); - focus_indicator.right.win = + focus_indicator.right.window = create_window(RootWindow(ob_display, ob_screen), CWOverrideRedirect | CWBackPixel, &attr); - focus_indicator.bottom.win = + focus_indicator.bottom.window = create_window(RootWindow(ob_display, ob_screen), CWOverrideRedirect | CWBackPixel, &attr); @@ -83,13 +83,13 @@ void focus_cycle_indicator_startup(gboolean reconfig) stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.left)); stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.right)); stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.bottom)); - g_hash_table_insert(window_map, &focus_indicator.top.win, + g_hash_table_insert(window_map, &focus_indicator.top.window, &focus_indicator.top); - g_hash_table_insert(window_map, &focus_indicator.left.win, + g_hash_table_insert(window_map, &focus_indicator.left.window, &focus_indicator.left); - g_hash_table_insert(window_map, &focus_indicator.right.win, + g_hash_table_insert(window_map, &focus_indicator.right.window, &focus_indicator.right); - g_hash_table_insert(window_map, &focus_indicator.bottom.win, + g_hash_table_insert(window_map, &focus_indicator.bottom.window, &focus_indicator.bottom); color_white = RrColorNew(ob_rr_inst, 0xff, 0xff, 0xff); @@ -117,20 +117,20 @@ void focus_cycle_indicator_shutdown(gboolean reconfig) RrAppearanceFree(a_focus_indicator); - g_hash_table_remove(window_map, &focus_indicator.top.win); - g_hash_table_remove(window_map, &focus_indicator.left.win); - g_hash_table_remove(window_map, &focus_indicator.right.win); - g_hash_table_remove(window_map, &focus_indicator.bottom.win); + g_hash_table_remove(window_map, &focus_indicator.top.window); + g_hash_table_remove(window_map, &focus_indicator.left.window); + g_hash_table_remove(window_map, &focus_indicator.right.window); + g_hash_table_remove(window_map, &focus_indicator.bottom.window); stacking_remove(INTERNAL_AS_WINDOW(&focus_indicator.top)); stacking_remove(INTERNAL_AS_WINDOW(&focus_indicator.left)); stacking_remove(INTERNAL_AS_WINDOW(&focus_indicator.right)); stacking_remove(INTERNAL_AS_WINDOW(&focus_indicator.bottom)); - XDestroyWindow(ob_display, focus_indicator.top.win); - XDestroyWindow(ob_display, focus_indicator.left.win); - XDestroyWindow(ob_display, focus_indicator.right.win); - XDestroyWindow(ob_display, focus_indicator.bottom.win); + XDestroyWindow(ob_display, focus_indicator.top.window); + XDestroyWindow(ob_display, focus_indicator.left.window); + XDestroyWindow(ob_display, focus_indicator.right.window); + XDestroyWindow(ob_display, focus_indicator.bottom.window); } void focus_cycle_draw_indicator(ObClient *c) @@ -141,10 +141,10 @@ void focus_cycle_draw_indicator(ObClient *c) /* kill enter events cause by this unmapping */ ignore_start = event_start_ignore_all_enters(); - XUnmapWindow(ob_display, focus_indicator.top.win); - XUnmapWindow(ob_display, focus_indicator.left.win); - XUnmapWindow(ob_display, focus_indicator.right.win); - XUnmapWindow(ob_display, focus_indicator.bottom.win); + XUnmapWindow(ob_display, focus_indicator.top.window); + XUnmapWindow(ob_display, focus_indicator.left.window); + XUnmapWindow(ob_display, focus_indicator.right.window); + XUnmapWindow(ob_display, focus_indicator.bottom.window); event_end_ignore_all_enters(ignore_start); @@ -170,7 +170,7 @@ void focus_cycle_draw_indicator(ObClient *c) /* kill enter events cause by this moving */ ignore_start = event_start_ignore_all_enters(); - XMoveResizeWindow(ob_display, focus_indicator.top.win, + XMoveResizeWindow(ob_display, focus_indicator.top.window, x, y, w, h); a_focus_indicator->texture[0].data.lineart.x1 = 0; a_focus_indicator->texture[0].data.lineart.y1 = h-1; @@ -188,7 +188,7 @@ void focus_cycle_draw_indicator(ObClient *c) a_focus_indicator->texture[3].data.lineart.y1 = h-1; a_focus_indicator->texture[3].data.lineart.x2 = w - wr; a_focus_indicator->texture[3].data.lineart.y2 = h-1; - RrPaint(a_focus_indicator, focus_indicator.top.win, + RrPaint(a_focus_indicator, focus_indicator.top.window, w, h); x = c->frame->area.x; @@ -196,7 +196,7 @@ void focus_cycle_draw_indicator(ObClient *c) w = wl; h = c->frame->area.height; - XMoveResizeWindow(ob_display, focus_indicator.left.win, + XMoveResizeWindow(ob_display, focus_indicator.left.window, x, y, w, h); a_focus_indicator->texture[0].data.lineart.x1 = w-1; a_focus_indicator->texture[0].data.lineart.y1 = 0; @@ -214,7 +214,7 @@ void focus_cycle_draw_indicator(ObClient *c) a_focus_indicator->texture[3].data.lineart.y1 = wt-1; a_focus_indicator->texture[3].data.lineart.x2 = w-1; a_focus_indicator->texture[3].data.lineart.y2 = h - wb; - RrPaint(a_focus_indicator, focus_indicator.left.win, + RrPaint(a_focus_indicator, focus_indicator.left.window, w, h); x = c->frame->area.x + c->frame->area.width - wr; @@ -222,7 +222,7 @@ void focus_cycle_draw_indicator(ObClient *c) w = wr; h = c->frame->area.height ; - XMoveResizeWindow(ob_display, focus_indicator.right.win, + XMoveResizeWindow(ob_display, focus_indicator.right.window, x, y, w, h); a_focus_indicator->texture[0].data.lineart.x1 = 0; a_focus_indicator->texture[0].data.lineart.y1 = 0; @@ -240,7 +240,7 @@ void focus_cycle_draw_indicator(ObClient *c) a_focus_indicator->texture[3].data.lineart.y1 = wt-1; a_focus_indicator->texture[3].data.lineart.x2 = 0; a_focus_indicator->texture[3].data.lineart.y2 = h - wb; - RrPaint(a_focus_indicator, focus_indicator.right.win, + RrPaint(a_focus_indicator, focus_indicator.right.window, w, h); x = c->frame->area.x; @@ -248,7 +248,7 @@ void focus_cycle_draw_indicator(ObClient *c) w = c->frame->area.width; h = wb; - XMoveResizeWindow(ob_display, focus_indicator.bottom.win, + XMoveResizeWindow(ob_display, focus_indicator.bottom.window, x, y, w, h); a_focus_indicator->texture[0].data.lineart.x1 = 0; a_focus_indicator->texture[0].data.lineart.y1 = 0; @@ -266,13 +266,13 @@ void focus_cycle_draw_indicator(ObClient *c) a_focus_indicator->texture[3].data.lineart.y1 = 0; a_focus_indicator->texture[3].data.lineart.x2 = w - wr; a_focus_indicator->texture[3].data.lineart.y2 = 0; - RrPaint(a_focus_indicator, focus_indicator.bottom.win, + RrPaint(a_focus_indicator, focus_indicator.bottom.window, w, h); - XMapWindow(ob_display, focus_indicator.top.win); - XMapWindow(ob_display, focus_indicator.left.win); - XMapWindow(ob_display, focus_indicator.right.win); - XMapWindow(ob_display, focus_indicator.bottom.win); + XMapWindow(ob_display, focus_indicator.top.window); + XMapWindow(ob_display, focus_indicator.left.window); + XMapWindow(ob_display, focus_indicator.right.window); + XMapWindow(ob_display, focus_indicator.bottom.window); event_end_ignore_all_enters(ignore_start); diff --git a/openbox/window.c b/openbox/window.c index c5e6a4ea..3dd8f5ad 100644 --- a/openbox/window.c +++ b/openbox/window.c @@ -57,7 +57,7 @@ Window window_top(ObWindow *self) case Window_Client: return ((ObClient*)self)->frame->window; case Window_Internal: - return ((InternalWindow*)self)->win; + return ((InternalWindow*)self)->window; } g_assert_not_reached(); return None; diff --git a/openbox/window.h b/openbox/window.h index a172cfce..7a905dea 100644 --- a/openbox/window.h +++ b/openbox/window.h @@ -44,8 +44,8 @@ struct _ObWindow /* Wrapper for internal stuff. If its struct matches this then it can be used as an ObWindow */ typedef struct InternalWindow { - ObWindow obwin; - Window win; + Window_InternalType type; + Window window; } InternalWindow; #define WINDOW_IS_MENU(win) (((ObWindow*)win)->type == Window_Menu) -- cgit v1.2.3 From c313b219a226c3b968ff312b96120eef0c527d91 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Wed, 6 Feb 2008 21:35:55 -0500 Subject: you can create dialog windows called "prompts" which have a message and some buttons! they don't do anything interesting yet. --- Makefile.am | 2 + openbox/event.c | 5 + openbox/openbox.c | 3 + openbox/prompt.c | 286 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ openbox/prompt.h | 68 +++++++++++++ openbox/window.c | 4 + openbox/window.h | 7 +- po/POTFILES.in | 1 + render/font.c | 12 ++- render/render.c | 11 ++- render/render.h | 5 +- 11 files changed, 397 insertions(+), 7 deletions(-) create mode 100644 openbox/prompt.c create mode 100644 openbox/prompt.h diff --git a/Makefile.am b/Makefile.am index 9781ca84..df891ac0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -250,6 +250,8 @@ openbox_openbox_SOURCES = \ openbox/ping.h \ openbox/place.c \ openbox/place.h \ + openbox/prompt.c \ + openbox/prompt.h \ openbox/popup.c \ openbox/popup.h \ openbox/prop.c \ diff --git a/openbox/event.c b/openbox/event.c index 1b3c1c23..f41140eb 100644 --- a/openbox/event.c +++ b/openbox/event.c @@ -31,6 +31,7 @@ #include "frame.h" #include "grab.h" #include "menu.h" +#include "prompt.h" #include "menuframe.h" #include "keyboard.h" #include "modkeys.h" @@ -459,6 +460,7 @@ static void event_process(const XEvent *ec, gpointer data) ObWindow *obwin = NULL; XEvent ee, *e; ObEventData *ed = data; + ObPrompt *prompt = NULL; /* make a copy we can mangle */ ee = *ec; @@ -483,6 +485,9 @@ static void event_process(const XEvent *ec, gpointer data) case Window_Internal: /* we don't do anything with events directly on these windows */ break; + case Window_Prompt: + prompt = WINDOW_AS_PROMPT(obwin); + break; } } diff --git a/openbox/openbox.c b/openbox/openbox.c index 12106f6b..277294d0 100644 --- a/openbox/openbox.c +++ b/openbox/openbox.c @@ -46,6 +46,7 @@ #include "config.h" #include "ping.h" #include "mainloop.h" +#include "prompt.h" #include "gettext.h" #include "parser/parse.h" #include "render/render.h" @@ -314,6 +315,7 @@ gint main(gint argc, gchar **argv) grab_startup(reconfigure); group_startup(reconfigure); ping_startup(reconfigure); + prompt_startup(reconfigure); client_startup(reconfigure); dock_startup(reconfigure); moveresize_startup(reconfigure); @@ -373,6 +375,7 @@ gint main(gint argc, gchar **argv) moveresize_shutdown(reconfigure); dock_shutdown(reconfigure); client_shutdown(reconfigure); + prompt_shutdown(reconfigure); ping_shutdown(reconfigure); group_shutdown(reconfigure); grab_shutdown(reconfigure); diff --git a/openbox/prompt.c b/openbox/prompt.c new file mode 100644 index 00000000..38152411 --- /dev/null +++ b/openbox/prompt.c @@ -0,0 +1,286 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + prompt.c for the Openbox window manager + Copyright (c) 2008 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "prompt.h" +#include "openbox.h" +#include "screen.h" +#include "openbox.h" +#include "gettext.h" + +static GList *prompt_list = NULL; + +/* we construct these */ +static RrAppearance *prompt_a_button; +static RrAppearance *prompt_a_hover; +static RrAppearance *prompt_a_press; + +#define msg_appearance(self) (ob_rr_theme->osd_hilite_label) + +void prompt_startup(gboolean reconfig) +{ + RrColor *c_button, *c_hover, *c_press; + + prompt_a_button = RrAppearanceCopy(ob_rr_theme->a_focused_unpressed_close); + prompt_a_hover = RrAppearanceCopy(ob_rr_theme->a_hover_focused_close); + prompt_a_press = RrAppearanceCopy(ob_rr_theme->a_focused_pressed_close); + + c_button = prompt_a_button->texture[0].data.mask.color; + c_hover = prompt_a_button->texture[0].data.mask.color; + c_press = prompt_a_button->texture[0].data.mask.color; + + RrAppearanceRemoveTextures(prompt_a_button); + RrAppearanceRemoveTextures(prompt_a_hover); + RrAppearanceRemoveTextures(prompt_a_press); + + RrAppearanceAddTextures(prompt_a_button, 1); + RrAppearanceAddTextures(prompt_a_hover, 1); + RrAppearanceAddTextures(prompt_a_press, 1); + + /* totally cheating here.. */ + prompt_a_button->texture[0] = ob_rr_theme->osd_hilite_label->texture[0]; + prompt_a_hover->texture[0] = ob_rr_theme->osd_hilite_label->texture[0]; + prompt_a_press->texture[0] = ob_rr_theme->osd_hilite_label->texture[0]; + + prompt_a_button->texture[0].data.text.color = c_button; + prompt_a_hover->texture[0].data.text.color = c_hover; + prompt_a_press->texture[0].data.text.color = c_press; +} + +void prompt_shutdown(gboolean reconfig) +{ + RrAppearanceFree(prompt_a_button); + RrAppearanceFree(prompt_a_hover); + RrAppearanceFree(prompt_a_press); +} + +ObPrompt* prompt_new(const gchar *msg, const gchar *const *answers) +{ + ObPrompt *self; + XSetWindowAttributes attrib; + guint i; + const gchar *const *c; + + attrib.override_redirect = TRUE; + + self = g_new0(ObPrompt, 1); + self->ref = 1; + self->super.type = Window_Prompt; + self->super.window = XCreateWindow(ob_display, + RootWindow(ob_display, ob_screen), + 0, 0, 1, 1, 0, + CopyFromParent, InputOutput, + CopyFromParent, + CWOverrideRedirect, &attrib); + g_hash_table_insert(window_map, &self->super.window, + PROMPT_AS_WINDOW(self)); + + self->a_bg = RrAppearanceCopy(ob_rr_theme->osd_hilite_bg); + + self->msg.text = g_strdup(msg); + self->msg.window = XCreateWindow(ob_display, self->super.window, + 0, 0, 1, 1, 0, + CopyFromParent, InputOutput, + CopyFromParent, 0, NULL); + XMapWindow(ob_display, self->msg.window); + + self->n_buttons = 0; + for (c = answers; *c != NULL; ++c) + ++self->n_buttons; + + if (!self->n_buttons) + self->n_buttons = 1; + + self->button = g_new(ObPromptElement, self->n_buttons); + + if (!answers) { + g_assert(self->n_buttons == 1); /* should be set to this above.. */ + self->button[0].text = g_strdup(_("OK")); + } + else { + g_assert(self->n_buttons > 0); + for (i = 0; i < self->n_buttons; ++i) + self->button[i].text = g_strdup(answers[i]); + } + + for (i = 0; i < self->n_buttons; ++i) { + self->button[i].window = XCreateWindow(ob_display, self->super.window, + 0, 0, 1, 1, 0, + CopyFromParent, InputOutput, + CopyFromParent, 0, NULL); + XMapWindow(ob_display, self->button[i].window); + g_hash_table_insert(window_map, &self->button[i].window, + PROMPT_AS_WINDOW(self)); + } + + return self; +} + +void prompt_ref(ObPrompt *self) +{ + ++self->ref; +} + +void prompt_unref(ObPrompt *self) +{ + if (self && --self->ref == 0) { + guint i; + + for (i = 0; i < self->n_buttons; ++i) { + g_hash_table_remove(window_map, &self->button[i].window); + XDestroyWindow(ob_display, self->button[i].window); + } + + XDestroyWindow(ob_display, self->msg.window); + + RrAppearanceFree(self->a_bg); + + g_hash_table_remove(window_map, &self->super.window); + XDestroyWindow(ob_display, self->super.window); + g_free(self); + } +} + +static void prompt_layout(ObPrompt *self, const Rect *area) +{ + RrAppearance *a_msg = msg_appearance(self); + gint l, r, t, b; + guint i; + gint allbuttonsw, allbuttonsh, buttonx; + gint w, h; + + const gint OUTSIDE_MARGIN = 4; + const gint MSG_BUTTON_SEPARATION = 4; + const gint BUTTON_SEPARATION = 4; + + RrMargins(self->a_bg, &l, &t, &r, &b); + l += OUTSIDE_MARGIN; + t += OUTSIDE_MARGIN; + r += OUTSIDE_MARGIN; + b += OUTSIDE_MARGIN; + + /* find the button sizes and how much space we need for them */ + allbuttonsw = allbuttonsh = 0; + for (i = 0; i < self->n_buttons; ++i) { + gint bw, bh; + + prompt_a_button->texture[0].data.text.string = self->button[i].text; + prompt_a_hover->texture[0].data.text.string = self->button[i].text; + prompt_a_press->texture[0].data.text.string = self->button[i].text; + RrMinSize(prompt_a_button, &bw, &bh); + self->button[i].width = bw; + self->button[i].height = bh; + RrMinSize(prompt_a_hover, &bw, &bh); + self->button[i].width = MAX(self->button[i].width, bw); + self->button[i].height = MAX(self->button[i].height, bh); + RrMinSize(prompt_a_press, &bw, &bh); + self->button[i].width = MAX(self->button[i].width, bw); + self->button[i].height = MAX(self->button[i].height, bh); + + allbuttonsw += self->button[i].width + (i > 0 ? BUTTON_SEPARATION : 0); + allbuttonsh = MAX(allbuttonsh, self->button[i].height); + } + + self->msg_wbound = MAX(allbuttonsw, area->width*3/5); + + /* measure the text message area */ + a_msg->texture[0].data.text.string = self->msg.text; + a_msg->texture[0].data.text.maxwidth = self->msg_wbound; + RrMinSize(a_msg, &self->msg.width, &self->msg.height); + a_msg->texture[0].data.text.maxwidth = 0; + + /* width and height inside the outer margins */ + w = MAX(self->msg.width, allbuttonsw); + h = self->msg.height + MSG_BUTTON_SEPARATION + allbuttonsh; + + /* position the text message */ + self->msg.x = l + (w - self->msg.width) / 2; + self->msg.y = t; + + /* position the button buttons */ + buttonx = l + (w - allbuttonsw) / 2; + for (i = 0; i < self->n_buttons; ++i) { + self->button[i].x = buttonx; + buttonx += self->button[i].width + BUTTON_SEPARATION; + self->button[i].y = h - allbuttonsh; + self->button[i].y += (allbuttonsh - self->button[i].height) / 2; + } + + /* size and position the toplevel window */ + self->width = w + l + r; + self->height = h + t + b; + self->x = (area->width - self->width) / 2; + self->y = (area->height - self->height) / 2; + + /* move and resize the actual windows */ + XMoveResizeWindow(ob_display, self->super.window, + self->x, self->y, self->width, self->height); + XMoveResizeWindow(ob_display, self->msg.window, + self->msg.x, self->msg.y, + self->msg.width, self->msg.height); + for (i = 0; i < self->n_buttons; ++i) + XMoveResizeWindow(ob_display, self->button[i].window, + self->button[i].x, self->button[i].y, + self->button[i].width, self->button[i].height); +} + +static void render_button(ObPrompt *self, ObPromptElement *e) +{ + prompt_a_button->surface.parent = self->a_bg; + prompt_a_button->surface.parentx = e->x; + prompt_a_button->surface.parentx = e->y; + + prompt_a_button->texture[0].data.text.string = e->text; + RrPaint(prompt_a_button, e->window, e->width, e->height); +} + +static void render_all(ObPrompt *self) +{ + guint i; + + RrPaint(self->a_bg, self->super.window, self->width, self->height); + + msg_appearance()->surface.parent = self->a_bg; + msg_appearance()->surface.parentx = self->msg.x; + msg_appearance()->surface.parentx = self->msg.y; + + msg_appearance()->texture[0].data.text.string = self->msg.text; + msg_appearance()->texture[0].data.text.maxwidth = self->msg_wbound; + RrPaint(msg_appearance(), self->msg.window, + self->msg.width, self->msg.height); + msg_appearance()->texture[0].data.text.maxwidth = 0; + + for (i = 0; i < self->n_buttons; ++i) + render_button(self, &self->button[i]); +} + +void prompt_show(ObPrompt *self, const Rect *area) +{ + if (self->mapped) return; + + prompt_layout(self, area); + render_all(self); + XMapWindow(ob_display, self->super.window); + + self->mapped = TRUE; +} + +void prompt_hide(ObPrompt *self) +{ + XUnmapWindow(ob_display, self->super.window); + self->mapped = FALSE; +} diff --git a/openbox/prompt.h b/openbox/prompt.h new file mode 100644 index 00000000..6b27b8d8 --- /dev/null +++ b/openbox/prompt.h @@ -0,0 +1,68 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + prompt.h for the Openbox window manager + Copyright (c) 2008 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef ob__prompt_h +#define ob__prompt_h + +typedef struct _ObPrompt ObPrompt; +typedef struct _ObPromptElement ObPromptElement; + +#include "window.h" +#include "geom.h" +#include "render/render.h" +#include + +struct _ObPromptElement { + gchar *text; + Window window; + + gint x, y, width, height; +}; + +struct _ObPrompt +{ + InternalWindow super; + gint ref; + + /* keep a copy of this because we re-render things that may need it + (i.e. the buttons) */ + RrAppearance *a_bg; + + gboolean mapped; + gint x, y, width, height; + gint msg_wbound; + + ObPromptElement msg; + + /* one for each answer */ + ObPromptElement *button; + guint n_buttons; +}; + +void prompt_startup(gboolean reconfig); +void prompt_shutdown(gboolean reconfig); + +ObPrompt* prompt_new(const gchar *msg, const gchar *const *answers); +void prompt_ref(ObPrompt *self); +void prompt_unref(ObPrompt *self); + +/*! Show the prompt. It will be centered within the given area rectangle */ +void prompt_show(ObPrompt *self, const Rect *area); +void prompt_hide(ObPrompt *self); + +#endif diff --git a/openbox/window.c b/openbox/window.c index 3dd8f5ad..84afc4d8 100644 --- a/openbox/window.c +++ b/openbox/window.c @@ -22,6 +22,7 @@ #include "dock.h" #include "client.h" #include "frame.h" +#include "prompt.h" GHashTable *window_map; @@ -58,6 +59,8 @@ Window window_top(ObWindow *self) return ((ObClient*)self)->frame->window; case Window_Internal: return ((InternalWindow*)self)->window; + case Window_Prompt: + return ((ObPrompt*)self)->super.window; } g_assert_not_reached(); return None; @@ -77,6 +80,7 @@ ObStackingLayer window_layer(ObWindow *self) case Window_Client: return ((ObClient*)self)->layer; case Window_Internal: + case Window_Prompt: return OB_STACKING_LAYER_INTERNAL; } g_assert_not_reached(); diff --git a/openbox/window.h b/openbox/window.h index 7a905dea..76615c03 100644 --- a/openbox/window.h +++ b/openbox/window.h @@ -32,8 +32,9 @@ typedef enum { Window_Dock, Window_DockApp, /* used for events but not stacking */ Window_Client, - Window_Internal /* used for stacking but not events (except to filter + Window_Internal,/* used for stacking but not events (except to filter events on the root window) */ + Window_Prompt, } Window_InternalType; struct _ObWindow @@ -53,23 +54,27 @@ typedef struct InternalWindow { #define WINDOW_IS_DOCKAPP(win) (((ObWindow*)win)->type == Window_DockApp) #define WINDOW_IS_CLIENT(win) (((ObWindow*)win)->type == Window_Client) #define WINDOW_IS_INTERNAL(win) (((ObWindow*)win)->type == Window_Internal) +#define WINDOW_IS_PROMPT(win) (((ObWindow*)win)->type == Window_Prompt) struct _ObMenu; struct _ObDock; struct _ObDockApp; struct _ObClient; +struct _ObPrompt; #define WINDOW_AS_MENU(win) ((struct _ObMenuFrame*)win) #define WINDOW_AS_DOCK(win) ((struct _ObDock*)win) #define WINDOW_AS_DOCKAPP(win) ((struct _ObDockApp*)win) #define WINDOW_AS_CLIENT(win) ((struct _ObClient*)win) #define WINDOW_AS_INTERNAL(win) ((struct InternalWindow*)win) +#define WINDOW_AS_PROMPT(win) ((struct _ObPrompt*)win) #define MENU_AS_WINDOW(menu) ((ObWindow*)menu) #define DOCK_AS_WINDOW(dock) ((ObWindow*)dock) #define DOCKAPP_AS_WINDOW(dockapp) ((ObWindow*)dockapp) #define CLIENT_AS_WINDOW(client) ((ObWindow*)client) #define INTERNAL_AS_WINDOW(intern) ((ObWindow*)intern) +#define PROMPT_AS_WINDOW(prompt) ((ObWindow*)prompt) extern GHashTable *window_map; diff --git a/po/POTFILES.in b/po/POTFILES.in index f7918cec..294127c8 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -15,3 +15,4 @@ openbox/session.c openbox/startupnotify.c openbox/translate.c openbox/xerror.c +openbox/prompt.c diff --git a/render/font.c b/render/font.c index 369f262e..29d48215 100644 --- a/render/font.c +++ b/render/font.c @@ -109,6 +109,7 @@ RrFont *RrFontOpen(const RrInstance *inst, const gchar *name, gint size, /* setup the layout */ pango_layout_set_font_description(out->layout, out->font_desc); pango_layout_set_single_paragraph_mode(out->layout, TRUE); + pango_layout_set_wrap(out->layout, PANGO_WRAP_WORD_CHAR); /* get the ascent and descent */ measure_font(inst, out); @@ -139,12 +140,14 @@ void RrFontClose(RrFont *f) } static void font_measure_full(const RrFont *f, const gchar *str, - gint *x, gint *y, gint shadow_x, gint shadow_y) + gint *x, gint *y, gint shadow_x, gint shadow_y, + gint maxwidth) { PangoRectangle rect; pango_layout_set_text(f->layout, str, -1); - pango_layout_set_width(f->layout, -1); + pango_layout_set_width(f->layout, + (maxwidth <= 0 ? -1 : maxwidth * PANGO_SCALE)); /* pango_layout_get_pixel_extents lies! this is the right way to get the size of the text's area */ @@ -163,11 +166,12 @@ static void font_measure_full(const RrFont *f, const gchar *str, } RrSize *RrFontMeasureString(const RrFont *f, const gchar *str, - gint shadow_x, gint shadow_y) + gint shadow_x, gint shadow_y, gint maxwidth) { RrSize *size; size = g_new(RrSize, 1); - font_measure_full(f, str, &size->width, &size->height, shadow_x, shadow_y); + font_measure_full(f, str, &size->width, &size->height, shadow_x, shadow_y, + maxwidth); return size; } diff --git a/render/render.c b/render/render.c index 4119dc7f..2147df01 100644 --- a/render/render.c +++ b/render/render.c @@ -178,6 +178,14 @@ RrAppearance *RrAppearanceNew(const RrInstance *inst, gint numtex) return out; } +void RrAppearanceRemoveTextures(RrAppearance *a) +{ + gint i; + + g_free(a->texture); + a->textures = 0; +} + void RrAppearanceAddTextures(RrAppearance *a, gint numtex) { g_assert(a->textures == 0); @@ -378,7 +386,8 @@ gint RrMinWidth(RrAppearance *a) m = RrFontMeasureString(a->texture[i].data.text.font, a->texture[i].data.text.string, a->texture[i].data.text.shadow_offset_x, - a->texture[i].data.text.shadow_offset_y); + a->texture[i].data.text.shadow_offset_y, + a->texture[i].data.text.maxwidth); w = MAX(w, m->width); g_free(m); break; diff --git a/render/render.h b/render/render.h index 1f87c6e0..c812c316 100644 --- a/render/render.h +++ b/render/render.h @@ -141,6 +141,7 @@ struct _RrTextureText { gboolean shortcut; /*!< Underline a character */ guint shortcut_pos; /*!< Position in bytes of the character to underline */ RrEllipsizeMode ellipsize; + gint maxwidth; }; struct _RrPixmapMask { @@ -244,6 +245,7 @@ GC RrColorGC (RrColor *c); RrAppearance *RrAppearanceNew (const RrInstance *inst, gint numtex); RrAppearance *RrAppearanceCopy (RrAppearance *a); void RrAppearanceFree (RrAppearance *a); +void RrAppearanceRemoveTextures(RrAppearance *a); void RrAppearanceAddTextures(RrAppearance *a, gint numtex); RrFont *RrFontOpen (const RrInstance *inst, const gchar *name, @@ -251,7 +253,8 @@ RrFont *RrFontOpen (const RrInstance *inst, const gchar *name, RrFont *RrFontOpenDefault (const RrInstance *inst); void RrFontClose (RrFont *f); RrSize *RrFontMeasureString (const RrFont *f, const gchar *str, - gint shadow_offset_x, gint shadow_offset_y); + gint shadow_offset_x, gint shadow_offset_y, + gint maxwidth); gint RrFontHeight (const RrFont *f, gint shadow_offset_y); gint RrFontMaxCharWidth (const RrFont *f); -- cgit v1.2.3 From 7867ced6222e1edb9624bd25122a11b808164041 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Wed, 6 Feb 2008 22:12:54 -0500 Subject: allow font rendering to use multiple lines --- render/font.c | 84 +++++++++++++++++++++++++++++++++++++++++++-------------- render/render.c | 24 ++++++++++++++--- render/render.h | 5 +++- 3 files changed, 88 insertions(+), 25 deletions(-) diff --git a/render/font.c b/render/font.c index 29d48215..2f06c731 100644 --- a/render/font.c +++ b/render/font.c @@ -108,7 +108,6 @@ RrFont *RrFontOpen(const RrInstance *inst, const gchar *name, gint size, /* setup the layout */ pango_layout_set_font_description(out->layout, out->font_desc); - pango_layout_set_single_paragraph_mode(out->layout, TRUE); pango_layout_set_wrap(out->layout, PANGO_WRAP_WORD_CHAR); /* get the ascent and descent */ @@ -141,13 +140,20 @@ void RrFontClose(RrFont *f) static void font_measure_full(const RrFont *f, const gchar *str, gint *x, gint *y, gint shadow_x, gint shadow_y, - gint maxwidth) + gboolean flow, gint maxwidth) { PangoRectangle rect; pango_layout_set_text(f->layout, str, -1); - pango_layout_set_width(f->layout, - (maxwidth <= 0 ? -1 : maxwidth * PANGO_SCALE)); + if (flow) { + pango_layout_set_single_paragraph_mode(f->layout, FALSE); + pango_layout_set_width(f->layout, maxwidth * PANGO_SCALE); + } + else { + /* single line mode */ + pango_layout_set_single_paragraph_mode(f->layout, TRUE); + pango_layout_set_width(f->layout, -1); + } /* pango_layout_get_pixel_extents lies! this is the right way to get the size of the text's area */ @@ -166,12 +172,16 @@ static void font_measure_full(const RrFont *f, const gchar *str, } RrSize *RrFontMeasureString(const RrFont *f, const gchar *str, - gint shadow_x, gint shadow_y, gint maxwidth) + gint shadow_x, gint shadow_y, + gboolean flow, gint maxwidth) { RrSize *size; + + g_assert(!flow || maxwidth > 0); + size = g_new(RrSize, 1); font_measure_full(f, str, &size->width, &size->height, shadow_x, shadow_y, - maxwidth); + flow, maxwidth); return size; } @@ -212,16 +222,22 @@ void RrFontDraw(XftDraw *d, RrTextureText *t, RrRect *area) PangoAttrList *attrlist; PangoEllipsizeMode ell; - /* center the text vertically - We do this centering based on the 'baseline' since different fonts have - different top edges. It looks bad when the whole string is moved when 1 - character from a non-default language is included in the string */ - y = area->y + - font_calculate_baseline(t->font, area->height); + g_assert(!t->flow || t->maxwidth > 0); + + y = area->y; + if (!t->flow) + /* center the text vertically + We do this centering based on the 'baseline' since different fonts + have different top edges. It looks bad when the whole string is + moved when 1 character from a non-default language is included in + the string */ + y += font_calculate_baseline(t->font, area->height); /* the +2 and -4 leave a small blank edge on the sides */ x = area->x + 2; - w = area->width - 4; + w = area->width; + if (t->flow) w = MAX(w, t->maxwidth); + w -= 4; h = area->height; switch (t->ellipsize) { @@ -242,6 +258,7 @@ void RrFontDraw(XftDraw *d, RrTextureText *t, RrRect *area) pango_layout_set_text(t->font->layout, t->string, -1); pango_layout_set_width(t->font->layout, w * PANGO_SCALE); pango_layout_set_ellipsize(t->font->layout, ell); + pango_layout_set_single_paragraph_mode(t->font->layout, !t->flow); /* * * end of setting up the layout * * */ @@ -280,10 +297,23 @@ void RrFontDraw(XftDraw *d, RrTextureText *t, RrRect *area) c.pixel = t->shadow_color->pixel; /* see below... */ - pango_xft_render_layout_line - (d, &c, pango_layout_get_line(t->font->layout, 0), - (x + t->shadow_offset_x) * PANGO_SCALE, - (y + t->shadow_offset_y) * PANGO_SCALE); + if (!t->flow) { + pango_xft_render_layout_line + (d, &c, +#if PANGO_VERSION_MAJOR > 1 || \ + (PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 16) + pango_layout_get_line_readonly(t->font->layout, 0), +#else + pango_layout_get_line(t->font->layout, 0), +#endif + (x + t->shadow_offset_x) * PANGO_SCALE, + (y + t->shadow_offset_y) * PANGO_SCALE); + } + else { + pango_xft_render_layout(d, &c, t->font->layout, + (x + t->shadow_offset_x) * PANGO_SCALE, + (y + t->shadow_offset_y) * PANGO_SCALE); + } } c.color.red = t->color->r | t->color->r << 8; @@ -310,9 +340,23 @@ void RrFontDraw(XftDraw *d, RrTextureText *t, RrRect *area) /* layout_line() uses y to specify the baseline The line doesn't need to be freed, it's a part of the layout */ - pango_xft_render_layout_line - (d, &c, pango_layout_get_line(t->font->layout, 0), - x * PANGO_SCALE, y * PANGO_SCALE); + if (!t->flow) { + pango_xft_render_layout_line + (d, &c, +#if PANGO_VERSION_MAJOR > 1 || \ + (PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 16) + pango_layout_get_line_readonly(t->font->layout, 0), +#else + pango_layout_get_line(t->font->layout, 0), +#endif + x * PANGO_SCALE, + y * PANGO_SCALE); + } + else { + pango_xft_render_layout(d, &c, t->font->layout, + x * PANGO_SCALE, + y * PANGO_SCALE); + } if (t->shortcut) { t->font->shortcut_underline->start_index = 0; diff --git a/render/render.c b/render/render.c index 2147df01..75dead16 100644 --- a/render/render.c +++ b/render/render.c @@ -180,8 +180,6 @@ RrAppearance *RrAppearanceNew(const RrInstance *inst, gint numtex) void RrAppearanceRemoveTextures(RrAppearance *a) { - gint i; - g_free(a->texture); a->textures = 0; } @@ -387,6 +385,7 @@ gint RrMinWidth(RrAppearance *a) a->texture[i].data.text.string, a->texture[i].data.text.shadow_offset_x, a->texture[i].data.text.shadow_offset_y, + a->texture[i].data.text.flow, a->texture[i].data.text.maxwidth); w = MAX(w, m->width); g_free(m); @@ -413,6 +412,7 @@ gint RrMinHeight(RrAppearance *a) { gint i; gint l, t, r, b; + RrSize *m; gint h = 0; for (i = 0; i < a->textures; ++i) { @@ -423,8 +423,24 @@ gint RrMinHeight(RrAppearance *a) h = MAX(h, a->texture[i].data.mask.mask->height); break; case RR_TEXTURE_TEXT: - h += MAX(h, RrFontHeight(a->texture[i].data.text.font, - a->texture[i].data.text.shadow_offset_y)); + if (a->texture[i].data.text.flow) { + g_assert(a->texture[i].data.text.string != NULL); + + m = RrFontMeasureString + (a->texture[i].data.text.font, + a->texture[i].data.text.string, + a->texture[i].data.text.shadow_offset_x, + a->texture[i].data.text.shadow_offset_y, + a->texture[i].data.text.flow, + a->texture[i].data.text.maxwidth); + h += MAX(h, m->height); + g_free(m); + } + else + h += MAX(h, + RrFontHeight + (a->texture[i].data.text.font, + a->texture[i].data.text.shadow_offset_y)); break; case RR_TEXTURE_RGBA: h += MAX(h, a->texture[i].data.rgba.height); diff --git a/render/render.h b/render/render.h index c812c316..0034ad47 100644 --- a/render/render.h +++ b/render/render.h @@ -141,6 +141,7 @@ struct _RrTextureText { gboolean shortcut; /*!< Underline a character */ guint shortcut_pos; /*!< Position in bytes of the character to underline */ RrEllipsizeMode ellipsize; + gboolean flow; /* allow multiple lines. must set maxwidth below */ gint maxwidth; }; @@ -254,7 +255,7 @@ RrFont *RrFontOpenDefault (const RrInstance *inst); void RrFontClose (RrFont *f); RrSize *RrFontMeasureString (const RrFont *f, const gchar *str, gint shadow_offset_x, gint shadow_offset_y, - gint maxwidth); + gboolean flow, gint maxwidth); gint RrFontHeight (const RrFont *f, gint shadow_offset_y); gint RrFontMaxCharWidth (const RrFont *f); @@ -265,6 +266,8 @@ Pixmap RrPaintPixmap (RrAppearance *a, gint w, gint h); void RrPaint (RrAppearance *a, Window win, gint w, gint h); void RrMinSize (RrAppearance *a, gint *w, gint *h); gint RrMinWidth (RrAppearance *a); +/* For text textures, if flow is TRUE, then the string must be set before + calling this, otherwise it doesn't need to be */ gint RrMinHeight (RrAppearance *a); void RrMargins (RrAppearance *a, gint *l, gint *t, gint *r, gint *b); -- cgit v1.2.3 From 974e88581fcab366579d5dd682713aa6deb752a9 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Wed, 6 Feb 2008 22:16:15 -0500 Subject: give prompts a border, and fix how they are laid out. and make them use the multi-line text capabilities in render for the message --- openbox/prompt.c | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/openbox/prompt.c b/openbox/prompt.c index 38152411..d58402bd 100644 --- a/openbox/prompt.c +++ b/openbox/prompt.c @@ -28,8 +28,8 @@ static GList *prompt_list = NULL; static RrAppearance *prompt_a_button; static RrAppearance *prompt_a_hover; static RrAppearance *prompt_a_press; - -#define msg_appearance(self) (ob_rr_theme->osd_hilite_label) +/* we change the max width which would screw with others */ +static RrAppearance *prompt_a_msg; void prompt_startup(gboolean reconfig) { @@ -59,6 +59,9 @@ void prompt_startup(gboolean reconfig) prompt_a_button->texture[0].data.text.color = c_button; prompt_a_hover->texture[0].data.text.color = c_hover; prompt_a_press->texture[0].data.text.color = c_press; + + prompt_a_msg = RrAppearanceCopy(ob_rr_theme->osd_hilite_label); + prompt_a_msg->texture[0].data.text.flow = TRUE; } void prompt_shutdown(gboolean reconfig) @@ -66,6 +69,7 @@ void prompt_shutdown(gboolean reconfig) RrAppearanceFree(prompt_a_button); RrAppearanceFree(prompt_a_hover); RrAppearanceFree(prompt_a_press); + RrAppearanceFree(prompt_a_msg); } ObPrompt* prompt_new(const gchar *msg, const gchar *const *answers) @@ -76,6 +80,7 @@ ObPrompt* prompt_new(const gchar *msg, const gchar *const *answers) const gchar *const *c; attrib.override_redirect = TRUE; + attrib.border_pixel = RrColorPixel(ob_rr_theme->osd_border_color); self = g_new0(ObPrompt, 1); self->ref = 1; @@ -83,9 +88,11 @@ ObPrompt* prompt_new(const gchar *msg, const gchar *const *answers) self->super.window = XCreateWindow(ob_display, RootWindow(ob_display, ob_screen), 0, 0, 1, 1, 0, + 0, 0, 1, 1, ob_rr_theme->obwidth, CopyFromParent, InputOutput, CopyFromParent, - CWOverrideRedirect, &attrib); + CWOverrideRedirect | CWBorderPixel, + &attrib); g_hash_table_insert(window_map, &self->super.window, PROMPT_AS_WINDOW(self)); @@ -157,7 +164,6 @@ void prompt_unref(ObPrompt *self) static void prompt_layout(ObPrompt *self, const Rect *area) { - RrAppearance *a_msg = msg_appearance(self); gint l, r, t, b; guint i; gint allbuttonsw, allbuttonsh, buttonx; @@ -198,10 +204,11 @@ static void prompt_layout(ObPrompt *self, const Rect *area) self->msg_wbound = MAX(allbuttonsw, area->width*3/5); /* measure the text message area */ - a_msg->texture[0].data.text.string = self->msg.text; - a_msg->texture[0].data.text.maxwidth = self->msg_wbound; - RrMinSize(a_msg, &self->msg.width, &self->msg.height); - a_msg->texture[0].data.text.maxwidth = 0; + prompt_a_msg->texture[0].data.text.string = self->msg.text; + prompt_a_msg->texture[0].data.text.maxwidth = self->msg_wbound; + RrMinSize(prompt_a_msg, &self->msg.width, &self->msg.height); + + g_print("height %d\n", self->msg.height); /* width and height inside the outer margins */ w = MAX(self->msg.width, allbuttonsw); @@ -216,7 +223,7 @@ static void prompt_layout(ObPrompt *self, const Rect *area) for (i = 0; i < self->n_buttons; ++i) { self->button[i].x = buttonx; buttonx += self->button[i].width + BUTTON_SEPARATION; - self->button[i].y = h - allbuttonsh; + self->button[i].y = t + h - allbuttonsh; self->button[i].y += (allbuttonsh - self->button[i].height) / 2; } @@ -254,15 +261,13 @@ static void render_all(ObPrompt *self) RrPaint(self->a_bg, self->super.window, self->width, self->height); - msg_appearance()->surface.parent = self->a_bg; - msg_appearance()->surface.parentx = self->msg.x; - msg_appearance()->surface.parentx = self->msg.y; + prompt_a_msg->surface.parent = self->a_bg; + prompt_a_msg->surface.parentx = self->msg.x; + prompt_a_msg->surface.parentx = self->msg.y; - msg_appearance()->texture[0].data.text.string = self->msg.text; - msg_appearance()->texture[0].data.text.maxwidth = self->msg_wbound; - RrPaint(msg_appearance(), self->msg.window, - self->msg.width, self->msg.height); - msg_appearance()->texture[0].data.text.maxwidth = 0; + prompt_a_msg->texture[0].data.text.string = self->msg.text; + prompt_a_msg->texture[0].data.text.maxwidth = self->msg_wbound; + RrPaint(prompt_a_msg, self->msg.window, self->msg.width, self->msg.height); for (i = 0; i < self->n_buttons; ++i) render_button(self, &self->button[i]); -- cgit v1.2.3 From 457fdc5ccbb65dc4b5e6cd972e048e3218527b91 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Wed, 6 Feb 2008 22:49:55 -0500 Subject: Make ObPrompt windows get managed as clients, and make them able to reconfigure as well. --- openbox/client.c | 17 +++++++++--- openbox/client.h | 9 ++++++- openbox/event.c | 2 +- openbox/prompt.c | 78 ++++++++++++++++++++++++++++++++++++++++++++------------ openbox/prompt.h | 5 ++-- 5 files changed, 87 insertions(+), 24 deletions(-) diff --git a/openbox/client.c b/openbox/client.c index 371eb087..1eba49f8 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -32,6 +32,7 @@ #include "session.h" #include "event.h" #include "grab.h" +#include "prompt.h" #include "focus.h" #include "stacking.h" #include "openbox.h" @@ -211,13 +212,13 @@ void client_manage_all(void) if (attrib.override_redirect) continue; if (attrib.map_state != IsUnmapped) - client_manage(children[i]); + client_manage(children[i], NULL); } } XFree(children); } -void client_manage(Window window) +void client_manage(Window window, ObPrompt *prompt) { ObClient *self; XEvent e; @@ -280,6 +281,7 @@ void client_manage(Window window) self = g_new0(ObClient, 1); self->obwin.type = Window_Client; self->window = window; + self->prompt = prompt; /* non-zero defaults */ self->wmstate = WithdrawnState; /* make sure it gets updated first time */ @@ -299,7 +301,8 @@ void client_manage(Window window) /* specify that if we exit, the window should not be destroyed and should be reparented back to root automatically */ - XChangeSaveSet(ob_display, window, SetModeInsert); + if (!self->prompt) + XChangeSaveSet(ob_display, window, SetModeInsert); /* create the decoration frame for the client window */ self->frame = frame_new(self); @@ -700,7 +703,8 @@ void client_unmanage(ObClient *self) mouse_grab_for_client(self, FALSE); /* remove the window from our save set */ - XChangeSaveSet(ob_display, self->window, SetModeDelete); + if (!self->prompt) + XChangeSaveSet(ob_display, self->window, SetModeDelete); /* update the focus lists */ focus_order_remove(self); @@ -3342,6 +3346,11 @@ void client_close(ObClient *self) { if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return; + if (self->prompt) { + prompt_hide(self); + return; + } + /* in the case that the client provides no means to requesting that it close, we just kill it */ if (!self->delete_window) diff --git a/openbox/client.h b/openbox/client.h index 83fdc9af..ac6b3153 100644 --- a/openbox/client.h +++ b/openbox/client.h @@ -37,6 +37,7 @@ struct _ObFrame; struct _ObGroup; struct _ObSessionState; +struct _ObPrompt; typedef struct _ObClient ObClient; typedef struct _ObClientIcon ObClientIcon; @@ -82,6 +83,10 @@ struct _ObClient ObWindow obwin; Window window; + /*! If this client is managing an ObPrompt window, then this is set to the + prompt */ + struct _ObPrompt *prompt; + /*! The window's decorations. NULL while the window is being managed! */ struct _ObFrame *frame; @@ -325,8 +330,10 @@ void client_remove_destroy_notify(ObClientCallback func); /*! Manages all existing windows */ void client_manage_all(); /*! Manages a given window + @param prompt This specifies an ObPrompt which is being managed. It is + possible to manage Openbox-owned windows through this. */ -void client_manage(Window win); +void client_manage(Window win, struct _ObPrompt *prompt); /*! Unmanages all managed windows */ void client_unmanage_all(); /*! Unmanages a given client */ diff --git a/openbox/event.c b/openbox/event.c index f41140eb..0d7ff2a3 100644 --- a/openbox/event.c +++ b/openbox/event.c @@ -644,7 +644,7 @@ static void event_process(const XEvent *ec, gpointer data) else if (window == RootWindow(ob_display, ob_screen)) event_handle_root(e); else if (e->type == MapRequest) - client_manage(window); + client_manage(window, NULL); else if (e->type == MappingNotify) { /* keyboard layout changes for modifier mapping changes. reload the modifier map, and rebind all the key bindings as appropriate */ diff --git a/openbox/prompt.c b/openbox/prompt.c index d58402bd..debdc382 100644 --- a/openbox/prompt.c +++ b/openbox/prompt.c @@ -20,6 +20,8 @@ #include "openbox.h" #include "screen.h" #include "openbox.h" +#include "client.h" +#include "prop.h" #include "gettext.h" static GList *prompt_list = NULL; @@ -31,6 +33,10 @@ static RrAppearance *prompt_a_press; /* we change the max width which would screw with others */ static RrAppearance *prompt_a_msg; +static void prompt_layout(ObPrompt *self); +static void render_all(ObPrompt *self); +static void render_button(ObPrompt *self, ObPromptElement *e); + void prompt_startup(gboolean reconfig) { RrColor *c_button, *c_hover, *c_press; @@ -62,6 +68,15 @@ void prompt_startup(gboolean reconfig) prompt_a_msg = RrAppearanceCopy(ob_rr_theme->osd_hilite_label); prompt_a_msg->texture[0].data.text.flow = TRUE; + + if (reconfig) { + GList *it; + for (it = prompt_list; it; it = g_list_next(it)) { + ObPrompt *p = it->data; + prompt_layout(p); + render_all(p); + } + } } void prompt_shutdown(gboolean reconfig) @@ -79,7 +94,7 @@ ObPrompt* prompt_new(const gchar *msg, const gchar *const *answers) guint i; const gchar *const *c; - attrib.override_redirect = TRUE; + attrib.override_redirect = FALSE; attrib.border_pixel = RrColorPixel(ob_rr_theme->osd_border_color); self = g_new0(ObPrompt, 1); @@ -87,14 +102,14 @@ ObPrompt* prompt_new(const gchar *msg, const gchar *const *answers) self->super.type = Window_Prompt; self->super.window = XCreateWindow(ob_display, RootWindow(ob_display, ob_screen), - 0, 0, 1, 1, 0, 0, 0, 1, 1, ob_rr_theme->obwidth, CopyFromParent, InputOutput, CopyFromParent, CWOverrideRedirect | CWBorderPixel, &attrib); - g_hash_table_insert(window_map, &self->super.window, - PROMPT_AS_WINDOW(self)); + + PROP_SET32(self->super.window, net_wm_window_type, atom, + prop_atoms.net_wm_window_type_dialog); self->a_bg = RrAppearanceCopy(ob_rr_theme->osd_hilite_bg); @@ -134,6 +149,8 @@ ObPrompt* prompt_new(const gchar *msg, const gchar *const *answers) PROMPT_AS_WINDOW(self)); } + prompt_list = g_list_prepend(prompt_list, self); + return self; } @@ -147,6 +164,8 @@ void prompt_unref(ObPrompt *self) if (self && --self->ref == 0) { guint i; + prompt_list = g_list_remove(prompt_list, self); + for (i = 0; i < self->n_buttons; ++i) { g_hash_table_remove(window_map, &self->button[i].window); XDestroyWindow(ob_display, self->button[i].window); @@ -156,22 +175,23 @@ void prompt_unref(ObPrompt *self) RrAppearanceFree(self->a_bg); - g_hash_table_remove(window_map, &self->super.window); XDestroyWindow(ob_display, self->super.window); g_free(self); } } -static void prompt_layout(ObPrompt *self, const Rect *area) +static void prompt_layout(ObPrompt *self) { gint l, r, t, b; guint i; gint allbuttonsw, allbuttonsh, buttonx; gint w, h; + gint maxw; const gint OUTSIDE_MARGIN = 4; const gint MSG_BUTTON_SEPARATION = 4; const gint BUTTON_SEPARATION = 4; + const gint MAX_WIDTH = 600; RrMargins(self->a_bg, &l, &t, &r, &b); l += OUTSIDE_MARGIN; @@ -179,6 +199,12 @@ static void prompt_layout(ObPrompt *self, const Rect *area) r += OUTSIDE_MARGIN; b += OUTSIDE_MARGIN; + { + Rect *area = screen_physical_area_all_monitors(); + maxw = MIN(MAX_WIDTH, area->width*4/5); + g_free(area); + } + /* find the button sizes and how much space we need for them */ allbuttonsw = allbuttonsh = 0; for (i = 0; i < self->n_buttons; ++i) { @@ -201,15 +227,13 @@ static void prompt_layout(ObPrompt *self, const Rect *area) allbuttonsh = MAX(allbuttonsh, self->button[i].height); } - self->msg_wbound = MAX(allbuttonsw, area->width*3/5); + self->msg_wbound = MAX(allbuttonsw, maxw); /* measure the text message area */ prompt_a_msg->texture[0].data.text.string = self->msg.text; prompt_a_msg->texture[0].data.text.maxwidth = self->msg_wbound; RrMinSize(prompt_a_msg, &self->msg.width, &self->msg.height); - g_print("height %d\n", self->msg.height); - /* width and height inside the outer margins */ w = MAX(self->msg.width, allbuttonsw); h = self->msg.height + MSG_BUTTON_SEPARATION + allbuttonsh; @@ -230,12 +254,9 @@ static void prompt_layout(ObPrompt *self, const Rect *area) /* size and position the toplevel window */ self->width = w + l + r; self->height = h + t + b; - self->x = (area->width - self->width) / 2; - self->y = (area->height - self->height) / 2; /* move and resize the actual windows */ - XMoveResizeWindow(ob_display, self->super.window, - self->x, self->y, self->width, self->height); + XResizeWindow(ob_display, self->super.window, self->width, self->height); XMoveResizeWindow(ob_display, self->msg.window, self->msg.x, self->msg.y, self->msg.width, self->msg.height); @@ -273,13 +294,25 @@ static void render_all(ObPrompt *self) render_button(self, &self->button[i]); } -void prompt_show(ObPrompt *self, const Rect *area) +void prompt_show(ObPrompt *self, ObClient *parent) { + XSizeHints hints; + if (self->mapped) return; - prompt_layout(self, area); + prompt_layout(self); render_all(self); - XMapWindow(ob_display, self->super.window); + + /* you can't resize the prompt */ + hints.flags = PMinSize | PMaxSize; + hints.min_width = hints.max_width = self->width; + hints.min_height = hints.max_height = self->height; + XSetWMNormalHints(ob_display, self->super.window, &hints); + + XSetTransientForHint(ob_display, (parent ? parent->window : 0), + self->super.window); + + client_manage(self->super.window, self); self->mapped = TRUE; } @@ -289,3 +322,16 @@ void prompt_hide(ObPrompt *self) XUnmapWindow(ob_display, self->super.window); self->mapped = FALSE; } + +void prompt_hide_window(Window window) +{ + GList *it; + ObPrompt *p = NULL; + + for (it = prompt_list; it; it = g_list_next(it)) { + p = it->data; + if (p->super.window == window) break; + } + g_assert(it != NULL); + prompt_hide(p); +} diff --git a/openbox/prompt.h b/openbox/prompt.h index 6b27b8d8..8d0bc988 100644 --- a/openbox/prompt.h +++ b/openbox/prompt.h @@ -44,7 +44,7 @@ struct _ObPrompt RrAppearance *a_bg; gboolean mapped; - gint x, y, width, height; + gint width, height; gint msg_wbound; ObPromptElement msg; @@ -62,7 +62,8 @@ void prompt_ref(ObPrompt *self); void prompt_unref(ObPrompt *self); /*! Show the prompt. It will be centered within the given area rectangle */ -void prompt_show(ObPrompt *self, const Rect *area); +void prompt_show(ObPrompt *self, struct _ObClient *parent); void prompt_hide(ObPrompt *self); +void prompt_hide_window(Window window); #endif -- cgit v1.2.3 From 61b8f5243063622598ea21c26cccc220ea29a50c Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Wed, 6 Feb 2008 22:53:43 -0500 Subject: properly place the msg texture --- openbox/prompt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openbox/prompt.c b/openbox/prompt.c index debdc382..84023d7b 100644 --- a/openbox/prompt.c +++ b/openbox/prompt.c @@ -284,7 +284,7 @@ static void render_all(ObPrompt *self) prompt_a_msg->surface.parent = self->a_bg; prompt_a_msg->surface.parentx = self->msg.x; - prompt_a_msg->surface.parentx = self->msg.y; + prompt_a_msg->surface.parenty = self->msg.y; prompt_a_msg->texture[0].data.text.string = self->msg.text; prompt_a_msg->texture[0].data.text.maxwidth = self->msg_wbound; -- cgit v1.2.3 From a5005506a89ecffe13e04cbcda5c20a2fa6ba25d Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Wed, 6 Feb 2008 23:33:02 -0500 Subject: make the prompt buttons respond to button presses. keyboard input code is there too but not working yet. --- openbox/client.c | 4 +- openbox/event.c | 20 +++++++- openbox/misc.h | 1 + openbox/prompt.c | 138 +++++++++++++++++++++++++++++++++++++++++++++---------- openbox/prompt.h | 9 +++- 5 files changed, 145 insertions(+), 27 deletions(-) diff --git a/openbox/client.c b/openbox/client.c index 1eba49f8..cd26debb 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -272,6 +272,8 @@ void client_manage(Window window, ObPrompt *prompt) /* choose the events we want to receive on the CLIENT window */ attrib_set.event_mask = CLIENT_EVENTMASK; + if (prompt) + attrib_set.event_mask |= KeyPressMask; attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK; XChangeWindowAttributes(ob_display, window, CWEventMask|CWDontPropagate, &attrib_set); @@ -3347,7 +3349,7 @@ void client_close(ObClient *self) if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return; if (self->prompt) { - prompt_hide(self); + prompt_hide(self->prompt); return; } diff --git a/openbox/event.c b/openbox/event.c index 0d7ff2a3..b2643916 100644 --- a/openbox/event.c +++ b/openbox/event.c @@ -89,6 +89,7 @@ static void event_process(const XEvent *e, gpointer data); static void event_handle_root(XEvent *e); static gboolean event_handle_menu_keyboard(XEvent *e); static gboolean event_handle_menu(XEvent *e); +static void event_handle_prompt(ObPrompt *p, XEvent *e); static void event_handle_dock(ObDock *s, XEvent *e); static void event_handle_dockapp(ObDockApp *app, XEvent *e); static void event_handle_client(ObClient *c, XEvent *e); @@ -704,7 +705,9 @@ static void event_process(const XEvent *ec, gpointer data) } #endif - if (e->type == ButtonPress || e->type == ButtonRelease) { + if (prompt) + event_handle_prompt(prompt, e); + else if (e->type == ButtonPress || e->type == ButtonRelease) { /* If the button press was on some non-root window, or was physically on the root window, then process it */ if (window != RootWindow(ob_display, ob_screen) || @@ -1672,6 +1675,21 @@ static ObMenuFrame* find_active_or_last_menu(void) return ret; } +static void event_handle_prompt(ObPrompt *p, XEvent *e) +{ + g_print("prompt event\n"); + switch (e->type) { + case ButtonPress: + case ButtonRelease: + case MotionNotify: + prompt_mouse_event(p, e); + break; + case KeyPress: + prompt_key_event(p, e); + break; + } +} + static gboolean event_handle_menu_keyboard(XEvent *ev) { guint keycode, state; diff --git a/openbox/misc.h b/openbox/misc.h index 2b5584d7..01c2da1d 100644 --- a/openbox/misc.h +++ b/openbox/misc.h @@ -51,6 +51,7 @@ typedef enum OB_KEY_RIGHT, OB_KEY_UP, OB_KEY_DOWN, + OB_KEY_TAB, OB_NUM_KEYS } ObKey; diff --git a/openbox/prompt.c b/openbox/prompt.c index 84023d7b..a72b6a44 100644 --- a/openbox/prompt.c +++ b/openbox/prompt.c @@ -22,13 +22,14 @@ #include "openbox.h" #include "client.h" #include "prop.h" +#include "modkeys.h" #include "gettext.h" static GList *prompt_list = NULL; /* we construct these */ static RrAppearance *prompt_a_button; -static RrAppearance *prompt_a_hover; +static RrAppearance *prompt_a_focus; static RrAppearance *prompt_a_press; /* we change the max width which would screw with others */ static RrAppearance *prompt_a_msg; @@ -39,31 +40,31 @@ static void render_button(ObPrompt *self, ObPromptElement *e); void prompt_startup(gboolean reconfig) { - RrColor *c_button, *c_hover, *c_press; + RrColor *c_button, *c_focus, *c_press; prompt_a_button = RrAppearanceCopy(ob_rr_theme->a_focused_unpressed_close); - prompt_a_hover = RrAppearanceCopy(ob_rr_theme->a_hover_focused_close); + prompt_a_focus = RrAppearanceCopy(ob_rr_theme->a_hover_focused_close); prompt_a_press = RrAppearanceCopy(ob_rr_theme->a_focused_pressed_close); c_button = prompt_a_button->texture[0].data.mask.color; - c_hover = prompt_a_button->texture[0].data.mask.color; + c_focus = prompt_a_button->texture[0].data.mask.color; c_press = prompt_a_button->texture[0].data.mask.color; RrAppearanceRemoveTextures(prompt_a_button); - RrAppearanceRemoveTextures(prompt_a_hover); + RrAppearanceRemoveTextures(prompt_a_focus); RrAppearanceRemoveTextures(prompt_a_press); RrAppearanceAddTextures(prompt_a_button, 1); - RrAppearanceAddTextures(prompt_a_hover, 1); + RrAppearanceAddTextures(prompt_a_focus, 1); RrAppearanceAddTextures(prompt_a_press, 1); /* totally cheating here.. */ prompt_a_button->texture[0] = ob_rr_theme->osd_hilite_label->texture[0]; - prompt_a_hover->texture[0] = ob_rr_theme->osd_hilite_label->texture[0]; + prompt_a_focus->texture[0] = ob_rr_theme->osd_hilite_label->texture[0]; prompt_a_press->texture[0] = ob_rr_theme->osd_hilite_label->texture[0]; prompt_a_button->texture[0].data.text.color = c_button; - prompt_a_hover->texture[0].data.text.color = c_hover; + prompt_a_focus->texture[0].data.text.color = c_focus; prompt_a_press->texture[0].data.text.color = c_press; prompt_a_msg = RrAppearanceCopy(ob_rr_theme->osd_hilite_label); @@ -82,7 +83,7 @@ void prompt_startup(gboolean reconfig) void prompt_shutdown(gboolean reconfig) { RrAppearanceFree(prompt_a_button); - RrAppearanceFree(prompt_a_hover); + RrAppearanceFree(prompt_a_focus); RrAppearanceFree(prompt_a_press); RrAppearanceFree(prompt_a_msg); } @@ -108,6 +109,7 @@ ObPrompt* prompt_new(const gchar *msg, const gchar *const *answers) CWOverrideRedirect | CWBorderPixel, &attrib); + /* make it a dialog type window */ PROP_SET32(self->super.window, net_wm_window_type, atom, prop_atoms.net_wm_window_type_dialog); @@ -147,8 +149,15 @@ ObPrompt* prompt_new(const gchar *msg, const gchar *const *answers) XMapWindow(ob_display, self->button[i].window); g_hash_table_insert(window_map, &self->button[i].window, PROMPT_AS_WINDOW(self)); + + /* listen for button presses on the buttons */ + XSelectInput(ob_display, self->button[i].window, + ButtonPressMask | ButtonReleaseMask | ButtonMotionMask); } + /* set the focus to the first button */ + self->focus = &self->button[0]; + prompt_list = g_list_prepend(prompt_list, self); return self; @@ -211,12 +220,12 @@ static void prompt_layout(ObPrompt *self) gint bw, bh; prompt_a_button->texture[0].data.text.string = self->button[i].text; - prompt_a_hover->texture[0].data.text.string = self->button[i].text; + prompt_a_focus->texture[0].data.text.string = self->button[i].text; prompt_a_press->texture[0].data.text.string = self->button[i].text; RrMinSize(prompt_a_button, &bw, &bh); self->button[i].width = bw; self->button[i].height = bh; - RrMinSize(prompt_a_hover, &bw, &bh); + RrMinSize(prompt_a_focus, &bw, &bh); self->button[i].width = MAX(self->button[i].width, bw); self->button[i].height = MAX(self->button[i].height, bh); RrMinSize(prompt_a_press, &bw, &bh); @@ -268,12 +277,18 @@ static void prompt_layout(ObPrompt *self) static void render_button(ObPrompt *self, ObPromptElement *e) { - prompt_a_button->surface.parent = self->a_bg; - prompt_a_button->surface.parentx = e->x; - prompt_a_button->surface.parentx = e->y; + RrAppearance *a; + + if (e->pressed) a = prompt_a_press; + else if (self->focus == e) a = prompt_a_focus, g_print("focus!\n"); + else a = prompt_a_button; + + a->surface.parent = self->a_bg; + a->surface.parentx = e->x; + a->surface.parentx = e->y; - prompt_a_button->texture[0].data.text.string = e->text; - RrPaint(prompt_a_button, e->window, e->width, e->height); + a->texture[0].data.text.string = e->text; + RrPaint(a, e->window, e->width, e->height); } static void render_all(ObPrompt *self) @@ -323,15 +338,90 @@ void prompt_hide(ObPrompt *self) self->mapped = FALSE; } -void prompt_hide_window(Window window) +void prompt_key_event(ObPrompt *self, XEvent *e) +{ + gboolean shift; + guint shift_mask; + + if (e->type != KeyPress) return; + + g_print("key 0x%x 0x%x\n", e->xkey.keycode, ob_keycode(OB_KEY_TAB)); + + shift_mask = modkeys_key_to_mask(OB_MODKEY_KEY_SHIFT); + shift = !!(e->xkey.state & shift_mask); + + /* only accept shift */ + if (e->xkey.state != 0 && e->xkey.state != shift_mask) + return; + + if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE)) + prompt_hide(self); + else if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN)) { + /* XXX run stuff */ + prompt_hide(self); + } + else if (e->xkey.keycode == ob_keycode(OB_KEY_TAB)) { + guint i; + ObPromptElement *oldfocus; + + oldfocus = self->focus; + + for (i = 0; i < self->n_buttons; ++i) + if (self->focus == &self->button[i]) break; + i += (shift ? -1 : 1); + if (i < 0) i = self->n_buttons - 1; + else if (i >= self->n_buttons) i = 0; + self->focus = &self->button[i]; + + if (oldfocus != self->focus) render_button(self, oldfocus); + render_button(self, self->focus); + } +} + +void prompt_mouse_event(ObPrompt *self, XEvent *e) { - GList *it; - ObPrompt *p = NULL; + guint i; + ObPromptElement *but; + + if (e->type != ButtonPress && e->type != ButtonRelease && + e->type != MotionNotify) return; + + /* find the button */ + for (i = 0; i < self->n_buttons; ++i) + if (self->button[i].window == + (e->type == MotionNotify ? e->xmotion.window : e->xbutton.window)) + { + but = &self->button[i]; + break; + } + g_assert(but != NULL); + + if (e->type == ButtonPress) { + ObPromptElement *oldfocus; - for (it = prompt_list; it; it = g_list_next(it)) { - p = it->data; - if (p->super.window == window) break; + oldfocus = self->focus; + + but->pressed = TRUE; + self->focus = but; + + if (oldfocus != but) render_button(self, oldfocus); + render_button(self, but); + } + else if (e->type == ButtonRelease) { + if (but->pressed) { + /* XXX run stuff */ + prompt_hide(self); + } + } + else if (e->type == MotionNotify) { + gboolean press; + + press = (e->xmotion.x >= 0 && e->xmotion.y >= 0 && + e->xmotion.x < but->width && e->xmotion.y < but->height); + + if (press != but->pressed) { + but->pressed = press; + render_button(self, but); + } } - g_assert(it != NULL); - prompt_hide(p); } diff --git a/openbox/prompt.h b/openbox/prompt.h index 8d0bc988..02c1edd7 100644 --- a/openbox/prompt.h +++ b/openbox/prompt.h @@ -26,12 +26,14 @@ typedef struct _ObPromptElement ObPromptElement; #include "geom.h" #include "render/render.h" #include +#include struct _ObPromptElement { gchar *text; Window window; gint x, y, width, height; + gboolean pressed; }; struct _ObPrompt @@ -52,6 +54,9 @@ struct _ObPrompt /* one for each answer */ ObPromptElement *button; guint n_buttons; + + /* points to the button with the focus */ + ObPromptElement *focus; }; void prompt_startup(gboolean reconfig); @@ -64,6 +69,8 @@ void prompt_unref(ObPrompt *self); /*! Show the prompt. It will be centered within the given area rectangle */ void prompt_show(ObPrompt *self, struct _ObClient *parent); void prompt_hide(ObPrompt *self); -void prompt_hide_window(Window window); + +void prompt_key_event(ObPrompt *self, XEvent *e); +void prompt_mouse_event(ObPrompt *self, XEvent *e); #endif -- cgit v1.2.3 From deca45dadad1e0e1d5183341a601267f2deb1aa5 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Wed, 6 Feb 2008 23:44:15 -0500 Subject: key input works for ObPrompt windows now --- openbox/client.c | 8 ++++---- openbox/event.c | 3 ++- openbox/prompt.c | 14 +++++++++++++- openbox/prompt.h | 2 ++ 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/openbox/client.c b/openbox/client.c index cd26debb..500562b0 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -270,10 +270,10 @@ void client_manage(Window window, ObPrompt *prompt) map_time = event_get_server_time(); - /* choose the events we want to receive on the CLIENT window */ - attrib_set.event_mask = CLIENT_EVENTMASK; - if (prompt) - attrib_set.event_mask |= KeyPressMask; + /* choose the events we want to receive on the CLIENT window + (ObPrompt windows can request events too) */ + attrib_set.event_mask = CLIENT_EVENTMASK | + (prompt ? prompt->event_mask : 0); attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK; XChangeWindowAttributes(ob_display, window, CWEventMask|CWDontPropagate, &attrib_set); diff --git a/openbox/event.c b/openbox/event.c index b2643916..ddf78e4f 100644 --- a/openbox/event.c +++ b/openbox/event.c @@ -478,6 +478,8 @@ static void event_process(const XEvent *ec, gpointer data) break; case Window_Client: client = WINDOW_AS_CLIENT(obwin); + /* events on clients can be events on prompt windows too */ + prompt = client->prompt; break; case Window_Menu: /* not to be used for events */ @@ -1677,7 +1679,6 @@ static ObMenuFrame* find_active_or_last_menu(void) static void event_handle_prompt(ObPrompt *p, XEvent *e) { - g_print("prompt event\n"); switch (e->type) { case ButtonPress: case ButtonRelease: diff --git a/openbox/prompt.c b/openbox/prompt.c index a72b6a44..73f0ba33 100644 --- a/openbox/prompt.c +++ b/openbox/prompt.c @@ -63,6 +63,10 @@ void prompt_startup(gboolean reconfig) prompt_a_focus->texture[0] = ob_rr_theme->osd_hilite_label->texture[0]; prompt_a_press->texture[0] = ob_rr_theme->osd_hilite_label->texture[0]; + prompt_a_button->texture[0].data.text.justify = RR_JUSTIFY_CENTER; + prompt_a_focus->texture[0].data.text.justify = RR_JUSTIFY_CENTER; + prompt_a_press->texture[0].data.text.justify = RR_JUSTIFY_CENTER; + prompt_a_button->texture[0].data.text.color = c_button; prompt_a_focus->texture[0].data.text.color = c_focus; prompt_a_press->texture[0].data.text.color = c_press; @@ -113,6 +117,9 @@ ObPrompt* prompt_new(const gchar *msg, const gchar *const *answers) PROP_SET32(self->super.window, net_wm_window_type, atom, prop_atoms.net_wm_window_type_dialog); + /* listen for key presses on the window */ + self->event_mask = KeyPressMask; + self->a_bg = RrAppearanceCopy(ob_rr_theme->osd_hilite_bg); self->msg.text = g_strdup(msg); @@ -200,6 +207,8 @@ static void prompt_layout(ObPrompt *self) const gint OUTSIDE_MARGIN = 4; const gint MSG_BUTTON_SEPARATION = 4; const gint BUTTON_SEPARATION = 4; + const gint BUTTON_VMARGIN = 4; + const gint BUTTON_HMARGIN = 12; const gint MAX_WIDTH = 600; RrMargins(self->a_bg, &l, &t, &r, &b); @@ -232,6 +241,9 @@ static void prompt_layout(ObPrompt *self) self->button[i].width = MAX(self->button[i].width, bw); self->button[i].height = MAX(self->button[i].height, bh); + self->button[i].width += BUTTON_HMARGIN * 2; + self->button[i].height += BUTTON_VMARGIN * 2; + allbuttonsw += self->button[i].width + (i > 0 ? BUTTON_SEPARATION : 0); allbuttonsh = MAX(allbuttonsh, self->button[i].height); } @@ -280,7 +292,7 @@ static void render_button(ObPrompt *self, ObPromptElement *e) RrAppearance *a; if (e->pressed) a = prompt_a_press; - else if (self->focus == e) a = prompt_a_focus, g_print("focus!\n"); + else if (self->focus == e) a = prompt_a_focus; else a = prompt_a_button; a->surface.parent = self->a_bg; diff --git a/openbox/prompt.h b/openbox/prompt.h index 02c1edd7..b31a0c04 100644 --- a/openbox/prompt.h +++ b/openbox/prompt.h @@ -41,6 +41,8 @@ struct _ObPrompt InternalWindow super; gint ref; + guint event_mask; + /* keep a copy of this because we re-render things that may need it (i.e. the buttons) */ RrAppearance *a_bg; -- cgit v1.2.3 From ea28a9e7ab6c81547b2730268097c4ad7d814a80 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Wed, 6 Feb 2008 23:47:38 -0500 Subject: position the buttons in the bottom right instead of centering them --- openbox/prompt.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/openbox/prompt.c b/openbox/prompt.c index 73f0ba33..e63911ba 100644 --- a/openbox/prompt.c +++ b/openbox/prompt.c @@ -263,13 +263,13 @@ static void prompt_layout(ObPrompt *self) self->msg.x = l + (w - self->msg.width) / 2; self->msg.y = t; - /* position the button buttons */ - buttonx = l + (w - allbuttonsw) / 2; - for (i = 0; i < self->n_buttons; ++i) { - self->button[i].x = buttonx; - buttonx += self->button[i].width + BUTTON_SEPARATION; - self->button[i].y = t + h - allbuttonsh; - self->button[i].y += (allbuttonsh - self->button[i].height) / 2; + /* position the button buttons on the right of the dialog */ + buttonx = l + w; + for (i = self->n_buttons; i > 0; --i) { + self->button[i-1].x = buttonx - self->button[i-1].width; + buttonx -= self->button[i-1].width + BUTTON_SEPARATION; + self->button[i-1].y = t + h - allbuttonsh; + self->button[i-1].y += (allbuttonsh - self->button[i-1].height) / 2; } /* size and position the toplevel window */ -- cgit v1.2.3 From c7af49b5c564d6287a0293ae26fc713fb55b4dc8 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Thu, 7 Feb 2008 00:10:57 -0500 Subject: let you specify return codes for the different buttons in a prompt, and specify a callback function for the prompt for when it is closed --- openbox/client.c | 2 +- openbox/prompt.c | 78 ++++++++++++++++++++++++++++++++++++-------------------- openbox/prompt.h | 42 ++++++++++++++++++++++++++---- 3 files changed, 88 insertions(+), 34 deletions(-) diff --git a/openbox/client.c b/openbox/client.c index 500562b0..7add1222 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -3349,7 +3349,7 @@ void client_close(ObClient *self) if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return; if (self->prompt) { - prompt_hide(self->prompt); + prompt_cancel(self->prompt); return; } diff --git a/openbox/prompt.c b/openbox/prompt.c index e63911ba..ba5228a1 100644 --- a/openbox/prompt.c +++ b/openbox/prompt.c @@ -92,18 +92,24 @@ void prompt_shutdown(gboolean reconfig) RrAppearanceFree(prompt_a_msg); } -ObPrompt* prompt_new(const gchar *msg, const gchar *const *answers) +ObPrompt* prompt_new(const gchar *msg, + const ObPromptAnswer *answers, gint n_answers, + gint default_result, gint cancel_result, + ObPromptCallback func, gpointer data) { ObPrompt *self; XSetWindowAttributes attrib; - guint i; - const gchar *const *c; + gint i; attrib.override_redirect = FALSE; attrib.border_pixel = RrColorPixel(ob_rr_theme->osd_border_color); self = g_new0(ObPrompt, 1); self->ref = 1; + self->func = func; + self->data = data; + self->default_result = default_result; + self->cancel_result = cancel_result; self->super.type = Window_Prompt; self->super.window = XCreateWindow(ob_display, RootWindow(ob_display, ob_screen), @@ -120,8 +126,10 @@ ObPrompt* prompt_new(const gchar *msg, const gchar *const *answers) /* listen for key presses on the window */ self->event_mask = KeyPressMask; + /* we make a copy of this appearance for each prompt */ self->a_bg = RrAppearanceCopy(ob_rr_theme->osd_hilite_bg); + /* set up the text message widow */ self->msg.text = g_strdup(msg); self->msg.window = XCreateWindow(ob_display, self->super.window, 0, 0, 1, 1, 0, @@ -129,23 +137,24 @@ ObPrompt* prompt_new(const gchar *msg, const gchar *const *answers) CopyFromParent, 0, NULL); XMapWindow(ob_display, self->msg.window); - self->n_buttons = 0; - for (c = answers; *c != NULL; ++c) - ++self->n_buttons; + /* set up the buttons from the answers */ + self->n_buttons = n_answers; if (!self->n_buttons) self->n_buttons = 1; - self->button = g_new(ObPromptElement, self->n_buttons); + self->button = g_new0(ObPromptElement, self->n_buttons); - if (!answers) { + if (n_answers == 0) { g_assert(self->n_buttons == 1); /* should be set to this above.. */ self->button[0].text = g_strdup(_("OK")); } else { g_assert(self->n_buttons > 0); - for (i = 0; i < self->n_buttons; ++i) - self->button[i].text = g_strdup(answers[i]); + for (i = 0; i < self->n_buttons; ++i) { + self->button[i].text = g_strdup(answers[i].text); + self->button[i].result = answers[i].result; + } } for (i = 0; i < self->n_buttons; ++i) { @@ -162,9 +171,6 @@ ObPrompt* prompt_new(const gchar *msg, const gchar *const *answers) ButtonPressMask | ButtonReleaseMask | ButtonMotionMask); } - /* set the focus to the first button */ - self->focus = &self->button[0]; - prompt_list = g_list_prepend(prompt_list, self); return self; @@ -178,7 +184,7 @@ void prompt_ref(ObPrompt *self) void prompt_unref(ObPrompt *self) { if (self && --self->ref == 0) { - guint i; + gint i; prompt_list = g_list_remove(prompt_list, self); @@ -199,7 +205,7 @@ void prompt_unref(ObPrompt *self) static void prompt_layout(ObPrompt *self) { gint l, r, t, b; - guint i; + gint i; gint allbuttonsw, allbuttonsh, buttonx; gint w, h; gint maxw; @@ -265,11 +271,11 @@ static void prompt_layout(ObPrompt *self) /* position the button buttons on the right of the dialog */ buttonx = l + w; - for (i = self->n_buttons; i > 0; --i) { - self->button[i-1].x = buttonx - self->button[i-1].width; - buttonx -= self->button[i-1].width + BUTTON_SEPARATION; - self->button[i-1].y = t + h - allbuttonsh; - self->button[i-1].y += (allbuttonsh - self->button[i-1].height) / 2; + for (i = self->n_buttons - 1; i >= 0; --i) { + self->button[i].x = buttonx - self->button[i].width; + buttonx -= self->button[i].width + BUTTON_SEPARATION; + self->button[i].y = t + h - allbuttonsh; + self->button[i].y += (allbuttonsh - self->button[i].height) / 2; } /* size and position the toplevel window */ @@ -305,7 +311,7 @@ static void render_button(ObPrompt *self, ObPromptElement *e) static void render_all(ObPrompt *self) { - guint i; + gint i; RrPaint(self->a_bg, self->super.window, self->width, self->height); @@ -324,11 +330,17 @@ static void render_all(ObPrompt *self) void prompt_show(ObPrompt *self, ObClient *parent) { XSizeHints hints; + gint i; if (self->mapped) return; - prompt_layout(self); - render_all(self); + /* set the focused button (if not found then the first button is used) */ + self->focus = &self->button[0]; + for (i = 0; i < self->n_buttons; ++i) + if (self->button[i].result == self->default_result) { + self->focus = &self->button[i]; + break; + } /* you can't resize the prompt */ hints.flags = PMinSize | PMaxSize; @@ -339,6 +351,10 @@ void prompt_show(ObPrompt *self, ObClient *parent) XSetTransientForHint(ob_display, (parent ? parent->window : 0), self->super.window); + /* set up the dialog and render it */ + prompt_layout(self); + render_all(self); + client_manage(self->super.window, self); self->mapped = TRUE; @@ -367,13 +383,13 @@ void prompt_key_event(ObPrompt *self, XEvent *e) return; if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE)) - prompt_hide(self); + prompt_cancel(self); else if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN)) { - /* XXX run stuff */ + if (self->func) self->func(self, self->focus->result, self->data); prompt_hide(self); } else if (e->xkey.keycode == ob_keycode(OB_KEY_TAB)) { - guint i; + gint i; ObPromptElement *oldfocus; oldfocus = self->focus; @@ -392,7 +408,7 @@ void prompt_key_event(ObPrompt *self, XEvent *e) void prompt_mouse_event(ObPrompt *self, XEvent *e) { - guint i; + gint i; ObPromptElement *but; if (e->type != ButtonPress && e->type != ButtonRelease && @@ -421,7 +437,7 @@ void prompt_mouse_event(ObPrompt *self, XEvent *e) } else if (e->type == ButtonRelease) { if (but->pressed) { - /* XXX run stuff */ + if (self->func) self->func(self, but->result, self->data); prompt_hide(self); } } @@ -437,3 +453,9 @@ void prompt_mouse_event(ObPrompt *self, XEvent *e) } } } + +void prompt_cancel(ObPrompt *self) +{ + if (self->func) self->func(self, self->cancel_result, self->data); + prompt_hide(self); +} diff --git a/openbox/prompt.h b/openbox/prompt.h index b31a0c04..c470f101 100644 --- a/openbox/prompt.h +++ b/openbox/prompt.h @@ -19,21 +19,25 @@ #ifndef ob__prompt_h #define ob__prompt_h -typedef struct _ObPrompt ObPrompt; -typedef struct _ObPromptElement ObPromptElement; - #include "window.h" #include "geom.h" #include "render/render.h" #include #include +typedef struct _ObPrompt ObPrompt; +typedef struct _ObPromptElement ObPromptElement; +typedef struct _ObPromptAnswer ObPromptAnswer; + +typedef void (*ObPromptCallback)(ObPrompt *p, gint result, gpointer data); + struct _ObPromptElement { gchar *text; Window window; gint x, y, width, height; gboolean pressed; + gint result; }; struct _ObPrompt @@ -55,16 +59,43 @@ struct _ObPrompt /* one for each answer */ ObPromptElement *button; - guint n_buttons; + gint n_buttons; /* points to the button with the focus */ ObPromptElement *focus; + /* the default button to have selected */ + gint default_result; + /* the cancel result if the dialog is closed */ + gint cancel_result; + + ObPromptCallback func; + gpointer data; +}; + +struct _ObPromptAnswer { + const gchar *text; + gint result; }; void prompt_startup(gboolean reconfig); void prompt_shutdown(gboolean reconfig); -ObPrompt* prompt_new(const gchar *msg, const gchar *const *answers); +/*! Create a new prompt + @param answers A number of ObPromptAnswers which define the buttons which + will appear in the dialog from left to right, and the result + returned when they are selected. + @param n_answers The number of answers + @param default_result The result for the answer button selected by default + @param cancel_result The result that is given if the dialog is closed instead + of having a button presssed + @param func The callback function which is called when the dialog is closed + or a button is pressed + @param data User defined data which will be passed to the callback +*/ +ObPrompt* prompt_new(const gchar *msg, + const ObPromptAnswer *answers, gint n_answers, + gint default_result, gint cancel_result, + ObPromptCallback func, gpointer data); void prompt_ref(ObPrompt *self); void prompt_unref(ObPrompt *self); @@ -74,5 +105,6 @@ void prompt_hide(ObPrompt *self); void prompt_key_event(ObPrompt *self, XEvent *e); void prompt_mouse_event(ObPrompt *self, XEvent *e); +void prompt_cancel(ObPrompt *self); #endif -- cgit v1.2.3 From d714bb5708bb8b99d71803404af7af01029c5553 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Thu, 7 Feb 2008 00:49:08 -0500 Subject: prompt to kill windows when they are not responding --- openbox/client.c | 82 +++++++++++++++++++++++++++++++++++++++++++++---------- openbox/client.h | 5 +++- openbox/event.c | 13 +++++---- openbox/misc.h | 1 + openbox/openbox.c | 2 ++ openbox/prompt.c | 31 ++++++++++++++------- openbox/prompt.h | 4 +-- 7 files changed, 105 insertions(+), 33 deletions(-) diff --git a/openbox/client.c b/openbox/client.c index 7add1222..98ad7de2 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -105,6 +105,7 @@ static GSList *client_search_all_top_parents_internal(ObClient *self, ObStackingLayer layer); static void client_call_notifies(ObClient *self, GSList *list); static void client_ping_event(ObClient *self, gboolean dead); +static void client_prompt_kill(ObClient *self); void client_startup(gboolean reconfig) @@ -302,7 +303,8 @@ void client_manage(Window window, ObPrompt *prompt) client_setup_decor_and_functions(self, FALSE); /* specify that if we exit, the window should not be destroyed and - should be reparented back to root automatically */ + should be reparented back to root automatically, unless we are managing + an internal ObPrompt window */ if (!self->prompt) XChangeSaveSet(ob_display, window, SetModeInsert); @@ -704,7 +706,8 @@ void client_unmanage(ObClient *self) mouse_grab_for_client(self, FALSE); - /* remove the window from our save set */ + /* remove the window from our save set, unless we are managing an internal + ObPrompt window */ if (!self->prompt) XChangeSaveSet(ob_display, self->window, SetModeDelete); @@ -715,6 +718,10 @@ void client_unmanage(ObClient *self) focus_client = NULL; } + /* if we're prompting to kill the client, close that */ + prompt_unref(self->kill_prompt); + self->kill_prompt = NULL; + client_list = g_list_remove(client_list, self); stacking_remove(self); g_hash_table_remove(window_map, &self->window); @@ -1992,7 +1999,7 @@ void client_update_title(ObClient *self) if (self->not_responding) { data = visible; - if (self->close_tried_term) + if (self->kill_level > 0) visible = g_strdup_printf("%s - [%s]", data, _("Killing...")); else visible = g_strdup_printf("%s - [%s]", data, _("Not Responding")); @@ -2024,7 +2031,7 @@ void client_update_title(ObClient *self) if (self->not_responding) { data = visible; - if (self->close_tried_term) + if (self->kill_level > 0) visible = g_strdup_printf("%s - [%s]", data, _("Killing...")); else visible = g_strdup_printf("%s - [%s]", data, _("Not Responding")); @@ -3338,9 +3345,14 @@ static void client_ping_event(ObClient *self, gboolean dead) client_update_title(self); if (!dead) { - /* try kill it nicely the first time again, if it started responding - at some point */ - self->close_tried_term = FALSE; + /* it came back to life ! */ + + if (self->kill_prompt) { + prompt_unref(self->kill_prompt); + self->kill_prompt = NULL; + } + + self->kill_level = 0; } } @@ -3359,24 +3371,64 @@ void client_close(ObClient *self) /* don't use client_kill(), we should only kill based on PID in response to a lack of PING replies */ XKillClient(ob_display, self->window); - else if (self->not_responding) - client_kill(self); - else + else { /* request the client to close with WM_DELETE_WINDOW */ PROP_MSG_TO(self->window, self->window, wm_protocols, prop_atoms.wm_delete_window, event_curtime, 0, 0, 0, NoEventMask); + + if (self->not_responding) + client_prompt_kill(self); + } +} + +#define OB_KILL_RESULT_NO 0 +#define OB_KILL_RESULT_YES 1 + +static void client_kill_requested(ObPrompt *p, gint result, gpointer data) +{ + ObClient *self = data; + + if (result == OB_KILL_RESULT_YES) + client_kill(self); + + prompt_unref(self->kill_prompt); + self->kill_prompt = NULL; +} + +static void client_prompt_kill(ObClient *self) +{ + ObPromptAnswer answers[] = { + { _("No"), OB_KILL_RESULT_NO }, + { _("Yes"), OB_KILL_RESULT_YES } + }; + gchar *m; + + /* check if we're already prompting */ + if (self->kill_prompt) return; + + m = g_strdup_printf + (_("The window \"%s\" does not seem to be responding. Do you want to force it to exit?"), self->title); + + self->kill_prompt = prompt_new(m, answers, + sizeof(answers)/sizeof(answers[0]), + OB_KILL_RESULT_NO, /* default = no */ + OB_KILL_RESULT_NO, /* cancel = no */ + client_kill_requested, self); + prompt_show(self->kill_prompt, self); + + g_free(m); } void client_kill(ObClient *self) { if (!self->client_machine && self->pid) { /* running on the local host */ - if (!self->close_tried_term) { - ob_debug("killing window 0x%x with pid %lu, with SIGTERM\n", + if (self->kill_level == 0) { + ob_debug("killing window 0x%x with pid %lu, with SIGTERM", self->window, self->pid); kill(self->pid, SIGTERM); - self->close_tried_term = TRUE; + ++self->kill_level; /* show that we're trying to kill it */ client_update_title(self); @@ -3387,8 +3439,10 @@ void client_kill(ObClient *self) kill(self->pid, SIGKILL); /* kill -9 */ } } - else + else { + /* running on a remote host */ XKillClient(ob_display, self->window); + } } void client_hilite(ObClient *self, gboolean hilite) diff --git a/openbox/client.h b/openbox/client.h index ac6b3153..4e19bdf8 100644 --- a/openbox/client.h +++ b/openbox/client.h @@ -236,8 +236,11 @@ struct _ObClient /*! Indicates if the client is trying to close but has stopped responding to pings */ gboolean not_responding; + /*! A prompt shown when you are trying to close a client that is not + responding. It asks if you want to kill the client */ + struct _ObPrompt *kill_prompt; /*! We tried to close the window with a SIGTERM */ - gboolean close_tried_term; + gint kill_level; #ifdef SYNC /*! The client wants to sync during resizes */ diff --git a/openbox/event.c b/openbox/event.c index ddf78e4f..ea249712 100644 --- a/openbox/event.c +++ b/openbox/event.c @@ -89,7 +89,7 @@ static void event_process(const XEvent *e, gpointer data); static void event_handle_root(XEvent *e); static gboolean event_handle_menu_keyboard(XEvent *e); static gboolean event_handle_menu(XEvent *e); -static void event_handle_prompt(ObPrompt *p, XEvent *e); +static gboolean event_handle_prompt(ObPrompt *p, XEvent *e); static void event_handle_dock(ObDock *s, XEvent *e); static void event_handle_dockapp(ObDockApp *app, XEvent *e); static void event_handle_client(ObClient *c, XEvent *e); @@ -707,8 +707,8 @@ static void event_process(const XEvent *ec, gpointer data) } #endif - if (prompt) - event_handle_prompt(prompt, e); + if (prompt && event_handle_prompt(prompt, e)) + ; else if (e->type == ButtonPress || e->type == ButtonRelease) { /* If the button press was on some non-root window, or was physically on the root window, then process it */ @@ -1677,18 +1677,19 @@ static ObMenuFrame* find_active_or_last_menu(void) return ret; } -static void event_handle_prompt(ObPrompt *p, XEvent *e) +static gboolean event_handle_prompt(ObPrompt *p, XEvent *e) { switch (e->type) { case ButtonPress: case ButtonRelease: case MotionNotify: - prompt_mouse_event(p, e); + return prompt_mouse_event(p, e); break; case KeyPress: - prompt_key_event(p, e); + return prompt_key_event(p, e); break; } + return FALSE; } static gboolean event_handle_menu_keyboard(XEvent *ev) diff --git a/openbox/misc.h b/openbox/misc.h index 01c2da1d..c73c9265 100644 --- a/openbox/misc.h +++ b/openbox/misc.h @@ -52,6 +52,7 @@ typedef enum OB_KEY_UP, OB_KEY_DOWN, OB_KEY_TAB, + OB_KEY_SPACE, OB_NUM_KEYS } ObKey; diff --git a/openbox/openbox.c b/openbox/openbox.c index 277294d0..cb14187c 100644 --- a/openbox/openbox.c +++ b/openbox/openbox.c @@ -232,6 +232,8 @@ gint main(gint argc, gchar **argv) keys[OB_KEY_RIGHT] = modkeys_sym_to_code(XK_Right); keys[OB_KEY_UP] = modkeys_sym_to_code(XK_Up); keys[OB_KEY_DOWN] = modkeys_sym_to_code(XK_Down); + keys[OB_KEY_TAB] = modkeys_sym_to_code(XK_Tab); + keys[OB_KEY_SPACE] = modkeys_sym_to_code(XK_space); { ObParseInst *i; diff --git a/openbox/prompt.c b/openbox/prompt.c index ba5228a1..080dc9a8 100644 --- a/openbox/prompt.c +++ b/openbox/prompt.c @@ -348,8 +348,8 @@ void prompt_show(ObPrompt *self, ObClient *parent) hints.min_height = hints.max_height = self->height; XSetWMNormalHints(ob_display, self->super.window, &hints); - XSetTransientForHint(ob_display, (parent ? parent->window : 0), - self->super.window); + XSetTransientForHint(ob_display, self->super.window, + (parent ? parent->window : 0)); /* set up the dialog and render it */ prompt_layout(self); @@ -366,12 +366,12 @@ void prompt_hide(ObPrompt *self) self->mapped = FALSE; } -void prompt_key_event(ObPrompt *self, XEvent *e) +gboolean prompt_key_event(ObPrompt *self, XEvent *e) { gboolean shift; guint shift_mask; - if (e->type != KeyPress) return; + if (e->type != KeyPress) return FALSE; g_print("key 0x%x 0x%x\n", e->xkey.keycode, ob_keycode(OB_KEY_TAB)); @@ -380,23 +380,31 @@ void prompt_key_event(ObPrompt *self, XEvent *e) /* only accept shift */ if (e->xkey.state != 0 && e->xkey.state != shift_mask) - return; + return FALSE; if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE)) prompt_cancel(self); - else if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN)) { + else if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN) || + e->xkey.keycode == ob_keycode(OB_KEY_SPACE)) + { if (self->func) self->func(self, self->focus->result, self->data); prompt_hide(self); } - else if (e->xkey.keycode == ob_keycode(OB_KEY_TAB)) { + else if (e->xkey.keycode == ob_keycode(OB_KEY_TAB) || + e->xkey.keycode == ob_keycode(OB_KEY_LEFT) || + e->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) + { gint i; + gboolean left; ObPromptElement *oldfocus; + left = e->xkey.keycode == ob_keycode(OB_KEY_LEFT) || + (e->xkey.keycode == ob_keycode(OB_KEY_TAB) && shift); oldfocus = self->focus; for (i = 0; i < self->n_buttons; ++i) if (self->focus == &self->button[i]) break; - i += (shift ? -1 : 1); + i += (left ? -1 : 1); if (i < 0) i = self->n_buttons - 1; else if (i >= self->n_buttons) i = 0; self->focus = &self->button[i]; @@ -404,9 +412,10 @@ void prompt_key_event(ObPrompt *self, XEvent *e) if (oldfocus != self->focus) render_button(self, oldfocus); render_button(self, self->focus); } + return TRUE; } -void prompt_mouse_event(ObPrompt *self, XEvent *e) +gboolean prompt_mouse_event(ObPrompt *self, XEvent *e) { gint i; ObPromptElement *but; @@ -415,6 +424,7 @@ void prompt_mouse_event(ObPrompt *self, XEvent *e) e->type != MotionNotify) return; /* find the button */ + but = NULL; for (i = 0; i < self->n_buttons; ++i) if (self->button[i].window == (e->type == MotionNotify ? e->xmotion.window : e->xbutton.window)) @@ -422,7 +432,7 @@ void prompt_mouse_event(ObPrompt *self, XEvent *e) but = &self->button[i]; break; } - g_assert(but != NULL); + if (!but) return FALSE; if (e->type == ButtonPress) { ObPromptElement *oldfocus; @@ -452,6 +462,7 @@ void prompt_mouse_event(ObPrompt *self, XEvent *e) render_button(self, but); } } + return TRUE; } void prompt_cancel(ObPrompt *self) diff --git a/openbox/prompt.h b/openbox/prompt.h index c470f101..c24f0448 100644 --- a/openbox/prompt.h +++ b/openbox/prompt.h @@ -103,8 +103,8 @@ void prompt_unref(ObPrompt *self); void prompt_show(ObPrompt *self, struct _ObClient *parent); void prompt_hide(ObPrompt *self); -void prompt_key_event(ObPrompt *self, XEvent *e); -void prompt_mouse_event(ObPrompt *self, XEvent *e); +gboolean prompt_key_event(ObPrompt *self, XEvent *e); +gboolean prompt_mouse_event(ObPrompt *self, XEvent *e); void prompt_cancel(ObPrompt *self); #endif -- cgit v1.2.3 From 972e1fc5a32e7d798fb3023012e73af20b5b03c7 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Thu, 7 Feb 2008 01:03:05 -0500 Subject: if a prompt is already showing and you try show it again, then make it active. in the "kill this?" prompt use the window's original title without any of the openbox-appended-ness --- openbox/client.c | 35 +++++++++++++++++++---------------- openbox/client.h | 2 ++ openbox/prompt.c | 13 +++++++++++-- 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/openbox/client.c b/openbox/client.c index 98ad7de2..44e027f8 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -820,6 +820,7 @@ void client_unmanage(ObClient *self) g_free(self->wm_command); g_free(self->title); g_free(self->icon_title); + g_free(self->original_title); g_free(self->name); g_free(self->class); g_free(self->role); @@ -1974,6 +1975,7 @@ void client_update_title(ObClient *self) gchar *visible = NULL; g_free(self->title); + g_free(self->original_title); /* try netwm */ if (!PROP_GETS(self->window, net_wm_name, utf8, &data)) { @@ -1990,6 +1992,7 @@ void client_update_title(ObClient *self) data = g_strdup("Unnamed Window"); } } + self->original_title = g_strdup(data); if (self->client_machine) { visible = g_strdup_printf("%s (%s)", data, self->client_machine); @@ -3398,26 +3401,26 @@ static void client_kill_requested(ObPrompt *p, gint result, gpointer data) static void client_prompt_kill(ObClient *self) { - ObPromptAnswer answers[] = { - { _("No"), OB_KILL_RESULT_NO }, - { _("Yes"), OB_KILL_RESULT_YES } - }; - gchar *m; - /* check if we're already prompting */ - if (self->kill_prompt) return; + if (!self->kill_prompt) { + ObPromptAnswer answers[] = { + { _("No"), OB_KILL_RESULT_NO }, + { _("Yes"), OB_KILL_RESULT_YES } + }; + gchar *m; - m = g_strdup_printf - (_("The window \"%s\" does not seem to be responding. Do you want to force it to exit?"), self->title); + m = g_strdup_printf + (_("The window \"%s\" does not seem to be responding. Do you want to force it to exit?"), self->original_title); - self->kill_prompt = prompt_new(m, answers, - sizeof(answers)/sizeof(answers[0]), - OB_KILL_RESULT_NO, /* default = no */ - OB_KILL_RESULT_NO, /* cancel = no */ - client_kill_requested, self); - prompt_show(self->kill_prompt, self); + self->kill_prompt = prompt_new(m, answers, + sizeof(answers)/sizeof(answers[0]), + OB_KILL_RESULT_NO, /* default = no */ + OB_KILL_RESULT_NO, /* cancel = no */ + client_kill_requested, self); + g_free(m); + } - g_free(m); + prompt_show(self->kill_prompt, self); } void client_kill(ObClient *self) diff --git a/openbox/client.h b/openbox/client.h index 4e19bdf8..197fb061 100644 --- a/openbox/client.h +++ b/openbox/client.h @@ -120,6 +120,8 @@ struct _ObClient gchar *title; /*! Window title when iconified */ gchar *icon_title; + /*! The title as requested by the client, without any of our own changes */ + gchar *original_title; /*! Hostname of machine running the client */ gchar *client_machine; /*! The command used to run the program. Pre-XSMP window identification. */ diff --git a/openbox/prompt.c b/openbox/prompt.c index 080dc9a8..1c7e2261 100644 --- a/openbox/prompt.c +++ b/openbox/prompt.c @@ -23,6 +23,7 @@ #include "client.h" #include "prop.h" #include "modkeys.h" +#include "event.h" #include "gettext.h" static GList *prompt_list = NULL; @@ -332,7 +333,15 @@ void prompt_show(ObPrompt *self, ObClient *parent) XSizeHints hints; gint i; - if (self->mapped) return; + if (self->mapped) { + /* activate the prompt */ + PROP_MSG(self->super.window, net_active_window, + 1, /* from an application.. */ + event_curtime, + 0, + 0); + return; + } /* set the focused button (if not found then the first button is used) */ self->focus = &self->button[0]; @@ -421,7 +430,7 @@ gboolean prompt_mouse_event(ObPrompt *self, XEvent *e) ObPromptElement *but; if (e->type != ButtonPress && e->type != ButtonRelease && - e->type != MotionNotify) return; + e->type != MotionNotify) return FALSE; /* find the button */ but = NULL; -- cgit v1.2.3 From 7b4556b2111b03dcdca4a58e1bde94a66ba86806 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Thu, 7 Feb 2008 01:29:02 -0500 Subject: don't kill our own ObPrompt windows with kill actions --- openbox/client.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openbox/client.c b/openbox/client.c index 44e027f8..ed2e5b56 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -3425,6 +3425,9 @@ static void client_prompt_kill(ObClient *self) void client_kill(ObClient *self) { + /* don't kill our own windows */ + if (self->prompt) return; + if (!self->client_machine && self->pid) { /* running on the local host */ if (self->kill_level == 0) { -- cgit v1.2.3 From 0da49e82aea1e881792ffc45a48301a090af18f3 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Thu, 7 Feb 2008 01:34:34 -0500 Subject: when drawing and measuring strings in "flow" mode, don't ellipsize so that lines can wrap --- render/font.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/render/font.c b/render/font.c index 2f06c731..583c9f7d 100644 --- a/render/font.c +++ b/render/font.c @@ -148,11 +148,13 @@ static void font_measure_full(const RrFont *f, const gchar *str, if (flow) { pango_layout_set_single_paragraph_mode(f->layout, FALSE); pango_layout_set_width(f->layout, maxwidth * PANGO_SCALE); + pango_layout_set_ellipsize(f->layout, PANGO_ELLIPSIZE_NONE); } else { /* single line mode */ pango_layout_set_single_paragraph_mode(f->layout, TRUE); pango_layout_set_width(f->layout, -1); + pango_layout_set_ellipsize(f->layout, PANGO_ELLIPSIZE_MIDDLE); } /* pango_layout_get_pixel_extents lies! this is the right way to get the @@ -240,19 +242,23 @@ void RrFontDraw(XftDraw *d, RrTextureText *t, RrRect *area) w -= 4; h = area->height; - switch (t->ellipsize) { - case RR_ELLIPSIZE_NONE: + if (t->flow) ell = PANGO_ELLIPSIZE_NONE; - break; - case RR_ELLIPSIZE_START: - ell = PANGO_ELLIPSIZE_START; - break; - case RR_ELLIPSIZE_MIDDLE: - ell = PANGO_ELLIPSIZE_MIDDLE; - break; - case RR_ELLIPSIZE_END: - ell = PANGO_ELLIPSIZE_END; - break; + else { + switch (t->ellipsize) { + case RR_ELLIPSIZE_NONE: + ell = PANGO_ELLIPSIZE_NONE; + break; + case RR_ELLIPSIZE_START: + ell = PANGO_ELLIPSIZE_START; + break; + case RR_ELLIPSIZE_MIDDLE: + ell = PANGO_ELLIPSIZE_MIDDLE; + break; + case RR_ELLIPSIZE_END: + ell = PANGO_ELLIPSIZE_END; + break; + } } pango_layout_set_text(t->font->layout, t->string, -1); -- cgit v1.2.3 From 74a746e55f8c7faea0d71e9744532bda462b23bd Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Thu, 7 Feb 2008 01:36:05 -0500 Subject: use the hover and press button colors (this was a bug) --- openbox/prompt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openbox/prompt.c b/openbox/prompt.c index 1c7e2261..f391331c 100644 --- a/openbox/prompt.c +++ b/openbox/prompt.c @@ -48,8 +48,8 @@ void prompt_startup(gboolean reconfig) prompt_a_press = RrAppearanceCopy(ob_rr_theme->a_focused_pressed_close); c_button = prompt_a_button->texture[0].data.mask.color; - c_focus = prompt_a_button->texture[0].data.mask.color; - c_press = prompt_a_button->texture[0].data.mask.color; + c_focus = prompt_a_focus->texture[0].data.mask.color; + c_press = prompt_a_press->texture[0].data.mask.color; RrAppearanceRemoveTextures(prompt_a_button); RrAppearanceRemoveTextures(prompt_a_focus); -- cgit v1.2.3 From 4eecbeb611680cf98df68905a0d22fcf68ca94a5 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Thu, 7 Feb 2008 01:46:40 -0500 Subject: tell what signal is going to be sent to the client when killing through the dialog --- openbox/client.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/openbox/client.c b/openbox/client.c index ed2e5b56..c8c6c93b 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -3408,9 +3408,15 @@ static void client_prompt_kill(ObClient *self) { _("Yes"), OB_KILL_RESULT_YES } }; gchar *m; + const gchar *sig; + + if (self->kill_level == 0) + sig = "terminate"; + else + sig = "kill"; m = g_strdup_printf - (_("The window \"%s\" does not seem to be responding. Do you want to force it to exit?"), self->original_title); + (_("The window \"%s\" does not seem to be responding. Do you want to force it to exit by sending the %s signal?"), self->original_title, sig); self->kill_prompt = prompt_new(m, answers, sizeof(answers)/sizeof(answers[0]), -- cgit v1.2.3 From c778cf15cbc954d0f42ef9030d56c61bb494c1e0 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Mon, 11 Feb 2008 22:45:27 -0500 Subject: don't ping everything all the time. yay. ping when you close, and if it doesn't reply then show the kill prompt. also show a more correct prompt for windows connecting from non-local machines - ask to disconnect them from the X server. --- openbox/client.c | 68 +++++++++++++++++++++++++++++++++++--------------------- openbox/client.h | 3 +++ openbox/ping.c | 17 +++++++------- openbox/ping.h | 1 - 4 files changed, 54 insertions(+), 35 deletions(-) diff --git a/openbox/client.c b/openbox/client.c index c8c6c93b..62489c1d 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -619,15 +619,6 @@ void client_manage(Window window, ObPrompt *prompt) /* update the list hints */ client_set_list(); - /* watch for when the application stops responding. only do this for - normal windows, i.e. windows which have titlebars and close buttons - and things like that. - we don't need to stop pinging on unmanage, because it will be handled - automatically by the destroy callback! - */ - if (self->ping && client_normal(self)) - ping_start(self, client_ping_event); - /* free the ObAppSettings shallow copy */ g_free(settings); @@ -3344,18 +3335,23 @@ void client_shade(ObClient *self, gboolean shade) static void client_ping_event(ObClient *self, gboolean dead) { - self->not_responding = dead; - client_update_title(self); + if (self->not_responding != dead) { + self->not_responding = dead; + client_update_title(self); + + if (dead) + /* the client isn't responding, so ask to kill it */ + client_prompt_kill(self); + else { + /* it came back to life ! */ - if (!dead) { - /* it came back to life ! */ + if (self->kill_prompt) { + prompt_unref(self->kill_prompt); + self->kill_prompt = NULL; + } - if (self->kill_prompt) { - prompt_unref(self->kill_prompt); - self->kill_prompt = NULL; + self->kill_level = 0; } - - self->kill_level = 0; } } @@ -3363,6 +3359,7 @@ void client_close(ObClient *self) { if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return; + /* if closing an internal obprompt, that is just cancelling it */ if (self->prompt) { prompt_cancel(self->prompt); return; @@ -3380,6 +3377,14 @@ void client_close(ObClient *self) prop_atoms.wm_delete_window, event_curtime, 0, 0, 0, NoEventMask); + /* we're trying to close the window, so see if it is responding. if it + is not, then we will let them kill the window */ + if (self->ping) + ping_start(self, client_ping_event); + + /* if we already know the window isn't responding (maybe they clicked + no in the kill dialog but it hasn't come back to life), then show + the kill dialog */ if (self->not_responding) client_prompt_kill(self); } @@ -3408,15 +3413,22 @@ static void client_prompt_kill(ObClient *self) { _("Yes"), OB_KILL_RESULT_YES } }; gchar *m; - const gchar *sig; - if (self->kill_level == 0) - sig = "terminate"; + if (client_on_localhost(self)) { + const gchar *sig; + + if (self->kill_level == 0) + sig = "terminate"; + else + sig = "kill"; + + m = g_strdup_printf + (_("The window \"%s\" does not seem to be responding. Do you want to force it to exit by sending the %s signal?"), self->original_title, sig); + } else - sig = "kill"; + m = g_strdup_printf + (_("The window \"%s\" does not seem to be responding. Do you want to disconnect it from the X server?"), self->original_title); - m = g_strdup_printf - (_("The window \"%s\" does not seem to be responding. Do you want to force it to exit by sending the %s signal?"), self->original_title, sig); self->kill_prompt = prompt_new(m, answers, sizeof(answers)/sizeof(answers[0]), @@ -3434,7 +3446,7 @@ void client_kill(ObClient *self) /* don't kill our own windows */ if (self->prompt) return; - if (!self->client_machine && self->pid) { + if (client_on_localhost(self) && self->pid) { /* running on the local host */ if (self->kill_level == 0) { ob_debug("killing window 0x%x with pid %lu, with SIGTERM", @@ -4424,3 +4436,9 @@ gboolean client_has_group_siblings(ObClient *self) { return self->group && self->group->members->next; } + +/*! Returns TRUE if the client is running on the same machine as Openbox */ +gboolean client_on_localhost(ObClient *self) +{ + return self->client_machine == NULL; +} diff --git a/openbox/client.h b/openbox/client.h index 197fb061..c34d0ab6 100644 --- a/openbox/client.h +++ b/openbox/client.h @@ -729,4 +729,7 @@ ObClient* client_under_pointer(); gboolean client_has_group_siblings(ObClient *self); +/*! Returns TRUE if the client is running on the same machine as Openbox */ +gboolean client_on_localhost(ObClient *self); + #endif diff --git a/openbox/ping.c b/openbox/ping.c index 37b5d30c..748c0c82 100644 --- a/openbox/ping.c +++ b/openbox/ping.c @@ -38,7 +38,7 @@ static guint32 ping_next_id = 1; #define PING_TIMEOUT (G_USEC_PER_SEC * 3) /*! Warn the user after this many PING_TIMEOUT intervals */ -#define PING_TIMEOUT_WARN 3 +#define PING_TIMEOUT_WARN 1 static void ping_send(ObPingTarget *t); static void ping_end(ObClient *client, gpointer data); @@ -69,11 +69,12 @@ void ping_start(struct _ObClient *client, ObPingEventHandler h) { ObPingTarget *t; - /* make sure we're not already pinging the client */ - g_assert(g_hash_table_find(ping_ids, find_client, client) == NULL); - + /* make sure the client supports ping! */ g_assert(client->ping == TRUE); + /* make sure we're not already pinging the client */ + if (g_hash_table_find(ping_ids, find_client, client) != NULL) return; + t = g_new0(ObPingTarget, 1); t->client = client; t->h = h; @@ -89,11 +90,6 @@ void ping_start(struct _ObClient *client, ObPingEventHandler h) g_assert(g_hash_table_find(ping_ids, find_client, client) != NULL); } -void ping_stop(struct _ObClient *c) -{ - ping_end(c, NULL); -} - void ping_got_pong(guint32 id) { ObPingTarget *t; @@ -106,6 +102,9 @@ void ping_got_pong(guint32 id) t->h(t->client, FALSE); } t->waiting = 0; /* not waiting for a reply anymore */ + + /* we got a pong so we're happy now */ + ping_end(t->client, NULL); } else ob_debug("Got PONG with id %u but not waiting for one\n", id); diff --git a/openbox/ping.h b/openbox/ping.h index 1333ea0f..ceb0bdb0 100644 --- a/openbox/ping.h +++ b/openbox/ping.h @@ -37,7 +37,6 @@ void ping_startup(gboolean reconfigure); void ping_shutdown(gboolean reconfigure); void ping_start(struct _ObClient *c, ObPingEventHandler h); -void ping_stop(struct _ObClient *c); void ping_got_pong(guint32 id); -- cgit v1.2.3 From 415c0d2721149a18ec53de8ebc5b8313b176d4d7 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Thu, 14 Feb 2008 12:15:30 -0500 Subject: make ObPrompts resize and redraw correctly when reconfiguring and changing themes with them open --- openbox/prompt.c | 67 ++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 24 deletions(-) diff --git a/openbox/prompt.c b/openbox/prompt.c index f391331c..8c6d4cb8 100644 --- a/openbox/prompt.c +++ b/openbox/prompt.c @@ -29,6 +29,7 @@ static GList *prompt_list = NULL; /* we construct these */ +static RrAppearance *prompt_a_bg; static RrAppearance *prompt_a_button; static RrAppearance *prompt_a_focus; static RrAppearance *prompt_a_press; @@ -38,11 +39,15 @@ static RrAppearance *prompt_a_msg; static void prompt_layout(ObPrompt *self); static void render_all(ObPrompt *self); static void render_button(ObPrompt *self, ObPromptElement *e); +static void prompt_resize(ObPrompt *self, gint w, gint h); void prompt_startup(gboolean reconfig) { RrColor *c_button, *c_focus, *c_press; + /* note: this is not a copy, don't free it */ + prompt_a_bg = ob_rr_theme->osd_hilite_bg; + prompt_a_button = RrAppearanceCopy(ob_rr_theme->a_focused_unpressed_close); prompt_a_focus = RrAppearanceCopy(ob_rr_theme->a_hover_focused_close); prompt_a_press = RrAppearanceCopy(ob_rr_theme->a_focused_pressed_close); @@ -103,7 +108,6 @@ ObPrompt* prompt_new(const gchar *msg, gint i; attrib.override_redirect = FALSE; - attrib.border_pixel = RrColorPixel(ob_rr_theme->osd_border_color); self = g_new0(ObPrompt, 1); self->ref = 1; @@ -114,10 +118,10 @@ ObPrompt* prompt_new(const gchar *msg, self->super.type = Window_Prompt; self->super.window = XCreateWindow(ob_display, RootWindow(ob_display, ob_screen), - 0, 0, 1, 1, ob_rr_theme->obwidth, + 0, 0, 1, 1, 0, CopyFromParent, InputOutput, CopyFromParent, - CWOverrideRedirect | CWBorderPixel, + CWOverrideRedirect, &attrib); /* make it a dialog type window */ @@ -127,9 +131,6 @@ ObPrompt* prompt_new(const gchar *msg, /* listen for key presses on the window */ self->event_mask = KeyPressMask; - /* we make a copy of this appearance for each prompt */ - self->a_bg = RrAppearanceCopy(ob_rr_theme->osd_hilite_bg); - /* set up the text message widow */ self->msg.text = g_strdup(msg); self->msg.window = XCreateWindow(ob_display, self->super.window, @@ -195,9 +196,6 @@ void prompt_unref(ObPrompt *self) } XDestroyWindow(ob_display, self->msg.window); - - RrAppearanceFree(self->a_bg); - XDestroyWindow(ob_display, self->super.window); g_free(self); } @@ -218,7 +216,7 @@ static void prompt_layout(ObPrompt *self) const gint BUTTON_HMARGIN = 12; const gint MAX_WIDTH = 600; - RrMargins(self->a_bg, &l, &t, &r, &b); + RrMargins(prompt_a_bg, &l, &t, &r, &b); l += OUTSIDE_MARGIN; t += OUTSIDE_MARGIN; r += OUTSIDE_MARGIN; @@ -280,11 +278,9 @@ static void prompt_layout(ObPrompt *self) } /* size and position the toplevel window */ - self->width = w + l + r; - self->height = h + t + b; + prompt_resize(self, w + l + r, h + t + b); - /* move and resize the actual windows */ - XResizeWindow(ob_display, self->super.window, self->width, self->height); + /* move and resize the internal windows */ XMoveResizeWindow(ob_display, self->msg.window, self->msg.x, self->msg.y, self->msg.width, self->msg.height); @@ -294,6 +290,36 @@ static void prompt_layout(ObPrompt *self) self->button[i].width, self->button[i].height); } +static void prompt_resize(ObPrompt *self, gint w, gint h) +{ + XConfigureRequestEvent req; + XSizeHints hints; + + self->width = w; + self->height = h; + + /* the user can't resize the prompt */ + hints.flags = PMinSize | PMaxSize; + hints.min_width = hints.max_width = w; + hints.min_height = hints.max_height = h; + XSetWMNormalHints(ob_display, self->super.window, &hints); + + if (self->mapped) { + /* send a configure request like a normal client would */ + req.type = ConfigureRequest; + req.display = ob_display; + req.parent = RootWindow(ob_display, ob_screen); + req.window = self->super.window; + req.width = w; + req.height = h; + req.value_mask = CWWidth | CWHeight; + XSendEvent(req.display, req.window, FALSE, StructureNotifyMask, + (XEvent*)&req); + } + else + XResizeWindow(ob_display, self->super.window, w, h); +} + static void render_button(ObPrompt *self, ObPromptElement *e) { RrAppearance *a; @@ -302,7 +328,7 @@ static void render_button(ObPrompt *self, ObPromptElement *e) else if (self->focus == e) a = prompt_a_focus; else a = prompt_a_button; - a->surface.parent = self->a_bg; + a->surface.parent = prompt_a_bg; a->surface.parentx = e->x; a->surface.parentx = e->y; @@ -314,9 +340,9 @@ static void render_all(ObPrompt *self) { gint i; - RrPaint(self->a_bg, self->super.window, self->width, self->height); + RrPaint(prompt_a_bg, self->super.window, self->width, self->height); - prompt_a_msg->surface.parent = self->a_bg; + prompt_a_msg->surface.parent = prompt_a_bg; prompt_a_msg->surface.parentx = self->msg.x; prompt_a_msg->surface.parenty = self->msg.y; @@ -330,7 +356,6 @@ static void render_all(ObPrompt *self) void prompt_show(ObPrompt *self, ObClient *parent) { - XSizeHints hints; gint i; if (self->mapped) { @@ -351,12 +376,6 @@ void prompt_show(ObPrompt *self, ObClient *parent) break; } - /* you can't resize the prompt */ - hints.flags = PMinSize | PMaxSize; - hints.min_width = hints.max_width = self->width; - hints.min_height = hints.max_height = self->height; - XSetWMNormalHints(ob_display, self->super.window, &hints); - XSetTransientForHint(ob_display, self->super.window, (parent ? parent->window : 0)); -- cgit v1.2.3 From baf1a0919d4562c7f75b3b18e700236ca0b49c43 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Thu, 14 Feb 2008 12:31:51 -0500 Subject: fix min-size calculations for lineart textures --- render/render.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/render/render.c b/render/render.c index 75dead16..2813dda0 100644 --- a/render/render.c +++ b/render/render.c @@ -373,6 +373,8 @@ gint RrMinWidth(RrAppearance *a) gint l, t, r, b; gint w = 0; + RrMargins(a, &l, &t, &r, &b); + for (i = 0; i < a->textures; ++i) { switch (a->texture[i].type) { case RR_TEXTURE_NONE: @@ -394,14 +396,12 @@ gint RrMinWidth(RrAppearance *a) w += MAX(w, a->texture[i].data.rgba.width); break; case RR_TEXTURE_LINE_ART: - w += MAX(w, MAX(a->texture[i].data.lineart.x1, - a->texture[i].data.lineart.x2)); + w = MAX(w, MAX(a->texture[i].data.lineart.x1 - l - r, + a->texture[i].data.lineart.x2 - l - r)); break; } } - RrMargins(a, &l, &t, &r, &b); - w += l + r; if (w < 1) w = 1; @@ -415,6 +415,8 @@ gint RrMinHeight(RrAppearance *a) RrSize *m; gint h = 0; + RrMargins(a, &l, &t, &r, &b); + for (i = 0; i < a->textures; ++i) { switch (a->texture[i].type) { case RR_TEXTURE_NONE: @@ -446,14 +448,12 @@ gint RrMinHeight(RrAppearance *a) h += MAX(h, a->texture[i].data.rgba.height); break; case RR_TEXTURE_LINE_ART: - h += MAX(h, MAX(a->texture[i].data.lineart.y1, - a->texture[i].data.lineart.y2)); + h = MAX(h, MAX(a->texture[i].data.lineart.y1 - t - b, + a->texture[i].data.lineart.y2 - t - b)); break; } } - RrMargins(a, &l, &t, &r, &b); - h += t + b; if (h < 1) h = 1; -- cgit v1.2.3 From cd3aa44b6a78e84e75420d6cca2c32f117a096a4 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Thu, 14 Feb 2008 13:10:33 -0500 Subject: draw a box inside the key-focused button in an ObPrompt. also fix rendering the buttons when they are parent-relative. --- openbox/prompt.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 98 insertions(+), 13 deletions(-) diff --git a/openbox/prompt.c b/openbox/prompt.c index 8c6d4cb8..b964de29 100644 --- a/openbox/prompt.c +++ b/openbox/prompt.c @@ -33,9 +33,18 @@ static RrAppearance *prompt_a_bg; static RrAppearance *prompt_a_button; static RrAppearance *prompt_a_focus; static RrAppearance *prompt_a_press; +static RrAppearance *prompt_a_pfocus; /* we change the max width which would screw with others */ static RrAppearance *prompt_a_msg; +/* sizing stuff */ +#define OUTSIDE_MARGIN 4 +#define MSG_BUTTON_SEPARATION 4 +#define BUTTON_SEPARATION 4 +#define BUTTON_VMARGIN 4 +#define BUTTON_HMARGIN 12 +#define MAX_WIDTH 400 + static void prompt_layout(ObPrompt *self); static void render_all(ObPrompt *self); static void render_button(ObPrompt *self, ObPromptElement *e); @@ -43,7 +52,7 @@ static void prompt_resize(ObPrompt *self, gint w, gint h); void prompt_startup(gboolean reconfig) { - RrColor *c_button, *c_focus, *c_press; + RrColor *c_button, *c_focus, *c_press, *c_pfocus; /* note: this is not a copy, don't free it */ prompt_a_bg = ob_rr_theme->osd_hilite_bg; @@ -51,31 +60,50 @@ void prompt_startup(gboolean reconfig) prompt_a_button = RrAppearanceCopy(ob_rr_theme->a_focused_unpressed_close); prompt_a_focus = RrAppearanceCopy(ob_rr_theme->a_hover_focused_close); prompt_a_press = RrAppearanceCopy(ob_rr_theme->a_focused_pressed_close); + prompt_a_pfocus = RrAppearanceCopy(ob_rr_theme->a_focused_pressed_close); c_button = prompt_a_button->texture[0].data.mask.color; c_focus = prompt_a_focus->texture[0].data.mask.color; c_press = prompt_a_press->texture[0].data.mask.color; + c_pfocus = prompt_a_press->texture[0].data.mask.color; RrAppearanceRemoveTextures(prompt_a_button); RrAppearanceRemoveTextures(prompt_a_focus); RrAppearanceRemoveTextures(prompt_a_press); + RrAppearanceRemoveTextures(prompt_a_pfocus); + /* texture[0] is the text and texture[1-4] (for prompt_a_focus and + prompt_a_pfocus) is lineart to show where keyboard focus is */ RrAppearanceAddTextures(prompt_a_button, 1); - RrAppearanceAddTextures(prompt_a_focus, 1); + RrAppearanceAddTextures(prompt_a_focus, 5); RrAppearanceAddTextures(prompt_a_press, 1); + RrAppearanceAddTextures(prompt_a_pfocus, 5); /* totally cheating here.. */ prompt_a_button->texture[0] = ob_rr_theme->osd_hilite_label->texture[0]; prompt_a_focus->texture[0] = ob_rr_theme->osd_hilite_label->texture[0]; prompt_a_press->texture[0] = ob_rr_theme->osd_hilite_label->texture[0]; + prompt_a_pfocus->texture[0] = ob_rr_theme->osd_hilite_label->texture[0]; prompt_a_button->texture[0].data.text.justify = RR_JUSTIFY_CENTER; prompt_a_focus->texture[0].data.text.justify = RR_JUSTIFY_CENTER; prompt_a_press->texture[0].data.text.justify = RR_JUSTIFY_CENTER; + prompt_a_pfocus->texture[0].data.text.justify = RR_JUSTIFY_CENTER; prompt_a_button->texture[0].data.text.color = c_button; prompt_a_focus->texture[0].data.text.color = c_focus; prompt_a_press->texture[0].data.text.color = c_press; + prompt_a_pfocus->texture[0].data.text.color = c_press; + + prompt_a_focus->texture[1].data.lineart.color = c_focus; + prompt_a_focus->texture[2].data.lineart.color = c_focus; + prompt_a_focus->texture[3].data.lineart.color = c_focus; + prompt_a_focus->texture[4].data.lineart.color = c_focus; + + prompt_a_pfocus->texture[1].data.lineart.color = c_press; + prompt_a_pfocus->texture[2].data.lineart.color = c_press; + prompt_a_pfocus->texture[3].data.lineart.color = c_press; + prompt_a_pfocus->texture[4].data.lineart.color = c_press; prompt_a_msg = RrAppearanceCopy(ob_rr_theme->osd_hilite_label); prompt_a_msg->texture[0].data.text.flow = TRUE; @@ -95,6 +123,7 @@ void prompt_shutdown(gboolean reconfig) RrAppearanceFree(prompt_a_button); RrAppearanceFree(prompt_a_focus); RrAppearanceFree(prompt_a_press); + RrAppearanceFree(prompt_a_pfocus); RrAppearanceFree(prompt_a_msg); } @@ -209,13 +238,6 @@ static void prompt_layout(ObPrompt *self) gint w, h; gint maxw; - const gint OUTSIDE_MARGIN = 4; - const gint MSG_BUTTON_SEPARATION = 4; - const gint BUTTON_SEPARATION = 4; - const gint BUTTON_VMARGIN = 4; - const gint BUTTON_HMARGIN = 12; - const gint MAX_WIDTH = 600; - RrMargins(prompt_a_bg, &l, &t, &r, &b); l += OUTSIDE_MARGIN; t += OUTSIDE_MARGIN; @@ -236,15 +258,21 @@ static void prompt_layout(ObPrompt *self) prompt_a_button->texture[0].data.text.string = self->button[i].text; prompt_a_focus->texture[0].data.text.string = self->button[i].text; prompt_a_press->texture[0].data.text.string = self->button[i].text; + prompt_a_pfocus->texture[0].data.text.string = self->button[i].text; RrMinSize(prompt_a_button, &bw, &bh); self->button[i].width = bw; self->button[i].height = bh; RrMinSize(prompt_a_focus, &bw, &bh); + g_print("button w %d h %d\n", bw, bh); self->button[i].width = MAX(self->button[i].width, bw); self->button[i].height = MAX(self->button[i].height, bh); RrMinSize(prompt_a_press, &bw, &bh); self->button[i].width = MAX(self->button[i].width, bw); self->button[i].height = MAX(self->button[i].height, bh); + RrMinSize(prompt_a_pfocus, &bw, &bh); + self->button[i].width = MAX(self->button[i].width, bw); + self->button[i].height = MAX(self->button[i].height, bh); + self->button[i].width += BUTTON_HMARGIN * 2; self->button[i].height += BUTTON_VMARGIN * 2; @@ -320,20 +348,77 @@ static void prompt_resize(ObPrompt *self, gint w, gint h) XResizeWindow(ob_display, self->super.window, w, h); } +static void setup_button_focus_tex(ObPromptElement *e, RrAppearance *a, + gboolean on) +{ + gint l, r, t, b; + + if (!on) { + gint i; + + for (i = 1; i < 5; ++i) + a->texture[i].type = on ? RR_TEXTURE_LINE_ART : RR_TEXTURE_NONE; + } + + if (!on) return; + + RrMargins(a, &l, &t, &r, &b); + l += MIN(BUTTON_HMARGIN, BUTTON_VMARGIN) / 2; + r += MIN(BUTTON_HMARGIN, BUTTON_VMARGIN) / 2; + t += MIN(BUTTON_HMARGIN, BUTTON_VMARGIN) / 2; + b += MIN(BUTTON_HMARGIN, BUTTON_VMARGIN) / 2; + + /* top line */ + a->texture[1].data.lineart.x1 = l; + a->texture[1].data.lineart.x2 = e->width - r - 1; + a->texture[1].data.lineart.y1 = t; + a->texture[1].data.lineart.y2 = t; + + /* bottom line */ + a->texture[2].data.lineart.x1 = l; + a->texture[2].data.lineart.x2 = e->width - r - 1; + a->texture[2].data.lineart.y1 = e->height - b - 1; + a->texture[2].data.lineart.y2 = e->height - b - 1; + + /* left line */ + a->texture[3].data.lineart.x1 = l; + a->texture[3].data.lineart.x2 = l; + a->texture[3].data.lineart.y1 = t; + a->texture[3].data.lineart.y2 = e->height - b - 1; + + /* right line */ + a->texture[4].data.lineart.x1 = e->width - r - 1; + a->texture[4].data.lineart.x2 = e->width - r - 1; + a->texture[4].data.lineart.y1 = t; + a->texture[4].data.lineart.y2 = e->height - b - 1; + + g_print("setting x2 %d\n", e->width - r - 1); +} + static void render_button(ObPrompt *self, ObPromptElement *e) { RrAppearance *a; - if (e->pressed) a = prompt_a_press; - else if (self->focus == e) a = prompt_a_focus; - else a = prompt_a_button; + if (e->pressed && self->focus == e) a = prompt_a_pfocus; + else if (self->focus == e) a = prompt_a_focus; + else if (e->pressed) a = prompt_a_press; + else a = prompt_a_button; a->surface.parent = prompt_a_bg; a->surface.parentx = e->x; - a->surface.parentx = e->y; + a->surface.parenty = e->y; + + /* draw the keyfocus line */ + if (a == prompt_a_pfocus || a == prompt_a_focus) + setup_button_focus_tex(e, a, TRUE); a->texture[0].data.text.string = e->text; RrPaint(a, e->window, e->width, e->height); + + /* turn off the keyfocus line so that it doesn't affect size calculations + */ + if (a == prompt_a_pfocus || a == prompt_a_focus) + setup_button_focus_tex(e, a, FALSE); } static void render_all(ObPrompt *self) -- cgit v1.2.3 From 911413d86c771248994727cfd53a7aa2e122993c Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Thu, 14 Feb 2008 14:27:46 -0500 Subject: obprompts do not have a stacking layer, as they don't appear directly in the stacking list --- openbox/window.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openbox/window.c b/openbox/window.c index 84afc4d8..d312fc36 100644 --- a/openbox/window.c +++ b/openbox/window.c @@ -80,8 +80,11 @@ ObStackingLayer window_layer(ObWindow *self) case Window_Client: return ((ObClient*)self)->layer; case Window_Internal: - case Window_Prompt: return OB_STACKING_LAYER_INTERNAL; + case Window_Prompt: + /* not used directly for stacking, prompts are managed as clients */ + g_assert_not_reached(); + break; } g_assert_not_reached(); return None; -- cgit v1.2.3 From e06ddf9f22bca1aefd4e6664aa54eea13543aade Mon Sep 17 00:00:00 2001 From: Mikael Magnusson Date: Thu, 14 Feb 2008 15:46:02 +0100 Subject: put the char member at the bottom of the struct so that it doesnt mess up alignment for the other members (assuming the compiler doesn't pad the struct appropriately anyways) --- render/render.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/render/render.h b/render/render.h index 0034ad47..d9438edc 100644 --- a/render/render.h +++ b/render/render.h @@ -137,12 +137,12 @@ struct _RrTextureText { gint shadow_offset_x; gint shadow_offset_y; RrColor *shadow_color; - guchar shadow_alpha; gboolean shortcut; /*!< Underline a character */ guint shortcut_pos; /*!< Position in bytes of the character to underline */ RrEllipsizeMode ellipsize; gboolean flow; /* allow multiple lines. must set maxwidth below */ gint maxwidth; + guchar shadow_alpha; /* at the bottom to improve alignment */ }; struct _RrPixmapMask { -- cgit v1.2.3