move x_* functions to x.c
This commit is contained in:
		
							parent
							
								
									5bc68ffc4b
								
							
						
					
					
						commit
						0a04bceb6f
					
				
							
								
								
									
										9
									
								
								dunst.h
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								dunst.h
									
									
									
									
									
								
							| @ -36,4 +36,13 @@ extern DC *dc; | |||||||
| gboolean run(void *data); | gboolean run(void *data); | ||||||
| void wake_up(void); | void wake_up(void); | ||||||
| 
 | 
 | ||||||
|  | void check_timeouts(void); | ||||||
|  | void history_pop(void); | ||||||
|  | void usage(int exit_status); | ||||||
|  | void move_all_to_history(void); | ||||||
|  | void print_version(void); | ||||||
|  | char *extract_urls(const char *str); | ||||||
|  | void context_menu(void); | ||||||
|  | void wake_up(void); | ||||||
|  | void pause_signal_handler(int sig); | ||||||
| /* vim: set ts=8 sw=8 tw=0: */ | /* vim: set ts=8 sw=8 tw=0: */ | ||||||
|  | |||||||
							
								
								
									
										978
									
								
								x.c
									
									
									
									
									
								
							
							
						
						
									
										978
									
								
								x.c
									
									
									
									
									
								
							| @ -29,7 +29,10 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |||||||
| DEALINGS IN THE SOFTWARE. | DEALINGS IN THE SOFTWARE. | ||||||
| 
 | 
 | ||||||
| */ | */ | ||||||
| 
 | #include <math.h> | ||||||
|  | #include <sys/time.h> | ||||||
|  | #include <ctype.h> | ||||||
|  | #include <assert.h> | ||||||
| #include <locale.h> | #include <locale.h> | ||||||
| #include <stdarg.h> | #include <stdarg.h> | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| @ -41,8 +44,16 @@ DEALINGS IN THE SOFTWARE. | |||||||
| #include <X11/Xatom.h> | #include <X11/Xatom.h> | ||||||
| 
 | 
 | ||||||
| #include "x.h" | #include "x.h" | ||||||
|  | #include "utils.h" | ||||||
|  | #include "dunst.h" | ||||||
|  | #include "settings.h" | ||||||
|  | #include "notification.h" | ||||||
| 
 | 
 | ||||||
| xctx_t xctx; | xctx_t xctx; | ||||||
|  | bool dunst_grab_errored = false; | ||||||
|  | 
 | ||||||
|  | static void x_shortcut_setup_error_handler(void); | ||||||
|  | static int x_shortcut_tear_down_error_handler(void); | ||||||
| 
 | 
 | ||||||
| void | void | ||||||
| drawrect(DC * dc, int x, int y, unsigned int w, unsigned int h, bool fill, | drawrect(DC * dc, int x, int y, unsigned int w, unsigned int h, bool fill, | ||||||
| @ -290,4 +301,969 @@ int textw(DC * dc, const char *text) | |||||||
|         return textnw(dc, text, strlen(text)) + dc->font.height; |         return textnw(dc, text, strlen(text)) + dc->font.height; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // {{{ X
 | ||||||
|  | // {{{ X_MAINLOOP
 | ||||||
|  | 
 | ||||||
|  |         /*
 | ||||||
|  |          * Helper function to use glib's mainloop mechanic | ||||||
|  |          * with Xlib | ||||||
|  |          */ | ||||||
|  | gboolean x_mainloop_fd_prepare(GSource *source, gint *timeout) | ||||||
|  | { // {{{
 | ||||||
|  |         *timeout = -1; | ||||||
|  |         return false; | ||||||
|  | } | ||||||
|  | // }}}
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         /*
 | ||||||
|  |          * Helper function to use glib's mainloop mechanic | ||||||
|  |          * with Xlib | ||||||
|  |          */ | ||||||
|  | gboolean x_mainloop_fd_check(GSource *source) | ||||||
|  | { // {{{
 | ||||||
|  |         return XPending(xctx.dc->dpy) > 0; | ||||||
|  | } | ||||||
|  | // }}}
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         /*
 | ||||||
|  |          * Main Dispatcher for XEvents | ||||||
|  |          */ | ||||||
|  | gboolean x_mainloop_fd_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) | ||||||
|  | { // {{{
 | ||||||
|  |         XEvent ev; | ||||||
|  |         while (XPending(xctx.dc->dpy) > 0) { | ||||||
|  |                 XNextEvent(xctx.dc->dpy, &ev); | ||||||
|  |                 switch (ev.type) { | ||||||
|  |                 case Expose: | ||||||
|  |                         if (ev.xexpose.count == 0 && xctx.visible) { | ||||||
|  |                         } | ||||||
|  |                         break; | ||||||
|  |                 case SelectionNotify: | ||||||
|  |                         if (ev.xselection.property == xctx.utf8) | ||||||
|  |                                 break; | ||||||
|  |                 case VisibilityNotify: | ||||||
|  |                         if (ev.xvisibility.state != VisibilityUnobscured) | ||||||
|  |                                 XRaiseWindow(xctx.dc->dpy, xctx.win); | ||||||
|  |                         break; | ||||||
|  |                 case ButtonPress: | ||||||
|  |                         if (ev.xbutton.window == xctx.win) { | ||||||
|  |                                 x_handle_click(ev); | ||||||
|  |                         } | ||||||
|  |                         break; | ||||||
|  |                 case KeyPress: | ||||||
|  |                         if (settings.close_ks.str | ||||||
|  |                             && XLookupKeysym(&ev.xkey, 0) == settings.close_ks.sym | ||||||
|  |                             && settings.close_ks.mask == ev.xkey.state) { | ||||||
|  |                                 if (displayed) { | ||||||
|  |                                         notification_close(g_queue_peek_head_link(displayed)->data, 2); | ||||||
|  |                                 } | ||||||
|  |                         } | ||||||
|  |                         if (settings.history_ks.str | ||||||
|  |                             && XLookupKeysym(&ev.xkey, 0) == settings.history_ks.sym | ||||||
|  |                             && settings.history_ks.mask == ev.xkey.state) { | ||||||
|  |                                 history_pop(); | ||||||
|  |                         } | ||||||
|  |                         if (settings.close_all_ks.str | ||||||
|  |                             && XLookupKeysym(&ev.xkey, 0) == settings.close_all_ks.sym | ||||||
|  |                             && settings.close_all_ks.mask == ev.xkey.state) { | ||||||
|  |                                 move_all_to_history(); | ||||||
|  |                         } | ||||||
|  |                         if (settings.context_ks.str | ||||||
|  |                             && XLookupKeysym(&ev.xkey, 0) == settings.context_ks.sym | ||||||
|  |                             && settings.context_ks.mask == ev.xkey.state) { | ||||||
|  |                                 context_menu(); | ||||||
|  |                         } | ||||||
|  |                         break; | ||||||
|  |                 } | ||||||
|  |         } | ||||||
|  |         return true; | ||||||
|  | } | ||||||
|  | // }}}
 | ||||||
|  | 
 | ||||||
|  | // }}}
 | ||||||
|  | 
 | ||||||
|  | // {{{ X_MISC
 | ||||||
|  | 
 | ||||||
|  |         /*
 | ||||||
|  |          * Check whether the user is currently idle. | ||||||
|  |          */ | ||||||
|  | bool x_is_idle(void) | ||||||
|  | { // {{{
 | ||||||
|  |         XScreenSaverQueryInfo(xctx.dc->dpy, DefaultRootWindow(xctx.dc->dpy), | ||||||
|  |                               xctx.screensaver_info); | ||||||
|  |         if (settings.idle_threshold == 0) { | ||||||
|  |                 return false; | ||||||
|  |         } | ||||||
|  |         return xctx.screensaver_info->idle / 1000 > settings.idle_threshold; | ||||||
|  | } | ||||||
|  | // }}}
 | ||||||
|  | 
 | ||||||
|  | /* TODO move to x_mainloop_* */ | ||||||
|  |         /*
 | ||||||
|  |          * Handle incoming mouse click events | ||||||
|  |          */ | ||||||
|  | void x_handle_click(XEvent ev) | ||||||
|  | { // {{{
 | ||||||
|  |         if (ev.xbutton.button == Button3) { | ||||||
|  |                 move_all_to_history(); | ||||||
|  | 
 | ||||||
|  |                 return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (ev.xbutton.button == Button1) { | ||||||
|  |                 int y = settings.separator_height; | ||||||
|  |                 notification *n = NULL; | ||||||
|  |                 for (GList *iter = g_queue_peek_head_link(displayed); iter; iter = iter->next) { | ||||||
|  |                         n = iter->data; | ||||||
|  |                         int text_h = MAX(xctx.font_h, settings.line_height) * n->line_count; | ||||||
|  |                         int padding = 2 * settings.h_padding; | ||||||
|  | 
 | ||||||
|  |                         int height = text_h + padding; | ||||||
|  | 
 | ||||||
|  |                         if (ev.xbutton.y > y && ev.xbutton.y < y + height) | ||||||
|  |                                 break; | ||||||
|  |                         else | ||||||
|  |                                 y += height + settings.separator_height; | ||||||
|  |                 } | ||||||
|  |                 if (n) | ||||||
|  |                         notification_close(n, 2); | ||||||
|  |         } | ||||||
|  | } | ||||||
|  | // }}}
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         /*
 | ||||||
|  |          * Return the window that currently has | ||||||
|  |          * the keyboard focus. | ||||||
|  |          */ | ||||||
|  | Window get_focused_window(void) | ||||||
|  | { // {{{
 | ||||||
|  |         Window focused = 0; | ||||||
|  |         Atom type; | ||||||
|  |         int format; | ||||||
|  |         unsigned long nitems, bytes_after; | ||||||
|  |         unsigned char *prop_return = NULL; | ||||||
|  |         Window root = RootWindow(xctx.dc->dpy, DefaultScreen(xctx.dc->dpy)); | ||||||
|  |         Atom netactivewindow = | ||||||
|  |             XInternAtom(xctx.dc->dpy, "_NET_ACTIVE_WINDOW", false); | ||||||
|  | 
 | ||||||
|  |         XGetWindowProperty(xctx.dc->dpy, root, netactivewindow, 0L, | ||||||
|  |                            sizeof(Window), false, XA_WINDOW, | ||||||
|  |                            &type, &format, &nitems, &bytes_after, &prop_return); | ||||||
|  |         if (prop_return) { | ||||||
|  |                 focused = *(Window *) prop_return; | ||||||
|  |                 XFree(prop_return); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return focused; | ||||||
|  | } | ||||||
|  | // }}}
 | ||||||
|  | 
 | ||||||
|  | #ifdef XINERAMA | ||||||
|  |         /*
 | ||||||
|  |          * Select the screen on which the Window | ||||||
|  |          * should be displayed. | ||||||
|  |          */ | ||||||
|  | int select_screen(XineramaScreenInfo * info, int info_len) | ||||||
|  | { // {{{
 | ||||||
|  |         if (settings.f_mode == FOLLOW_NONE) { | ||||||
|  |                 return settings.monitor >= 0 ? settings.monitor : XDefaultScreen(xctx.dc->dpy); | ||||||
|  | 
 | ||||||
|  |         } else { | ||||||
|  |                 int x, y; | ||||||
|  |                 assert(settings.f_mode == FOLLOW_MOUSE || settings.f_mode == FOLLOW_KEYBOARD); | ||||||
|  |                 Window root = RootWindow(xctx.dc->dpy, DefaultScreen(xctx.dc->dpy)); | ||||||
|  | 
 | ||||||
|  |                 if (settings.f_mode == FOLLOW_MOUSE) { | ||||||
|  |                         int dummy; | ||||||
|  |                         unsigned int dummy_ui; | ||||||
|  |                         Window dummy_win; | ||||||
|  | 
 | ||||||
|  |                         XQueryPointer(xctx.dc->dpy, root, &dummy_win, | ||||||
|  |                                       &dummy_win, &x, &y, &dummy, | ||||||
|  |                                       &dummy, &dummy_ui); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (settings.f_mode == FOLLOW_KEYBOARD) { | ||||||
|  | 
 | ||||||
|  |                         Window focused = get_focused_window(); | ||||||
|  | 
 | ||||||
|  |                         if (focused == 0) { | ||||||
|  |                                 /* something went wrong. Fallback to default */ | ||||||
|  |                                 return settings.monitor >= 0 ? settings.monitor : XDefaultScreen(xctx.dc->dpy); | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         Window child_return; | ||||||
|  |                         XTranslateCoordinates(xctx.dc->dpy, focused, root, | ||||||
|  |                                               0, 0, &x, &y, &child_return); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 for (int i = 0; i < info_len; i++) { | ||||||
|  |                         if (INRECT(x, y, info[i].x_org, | ||||||
|  |                                    info[i].y_org, | ||||||
|  |                                    info[i].width, info[i].height)) { | ||||||
|  |                                 return i; | ||||||
|  |                         } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 /* something seems to be wrong. Fallback to default */ | ||||||
|  |                 return settings.monitor >= 0 ? settings.monitor : XDefaultScreen(xctx.dc->dpy); | ||||||
|  |         } | ||||||
|  | } | ||||||
|  | // }}}
 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |         /*
 | ||||||
|  |          * Update the information about the monitor | ||||||
|  |          * geometry. | ||||||
|  |          */ | ||||||
|  | void x_screen_info(screen_info *scr) | ||||||
|  | { // {{{
 | ||||||
|  | #ifdef XINERAMA | ||||||
|  |         int n; | ||||||
|  |         XineramaScreenInfo *info; | ||||||
|  |         if ((info = XineramaQueryScreens(xctx.dc->dpy, &n))) { | ||||||
|  |                 int screen = select_screen(info, n); | ||||||
|  |                 if (screen >= n) { | ||||||
|  |                         /* invalid monitor, fallback to default */ | ||||||
|  |                         screen = 0; | ||||||
|  |                 } | ||||||
|  |                 scr->dim.x = info[screen].x_org; | ||||||
|  |                 scr->dim.y = info[screen].y_org; | ||||||
|  |                 scr->dim.h = info[screen].height; | ||||||
|  |                 scr->dim.w = info[screen].width; | ||||||
|  |                 XFree(info); | ||||||
|  |         } else | ||||||
|  | #endif | ||||||
|  |         { | ||||||
|  |                 scr->dim.x = 0; | ||||||
|  |                 scr->dim.y = 0; | ||||||
|  | 
 | ||||||
|  |                 int screen; | ||||||
|  |                 if (settings.monitor >= 0) | ||||||
|  |                         screen = settings.monitor; | ||||||
|  |                 else | ||||||
|  |                         screen = DefaultScreen(xctx.dc->dpy); | ||||||
|  | 
 | ||||||
|  |                 scr->dim.w = DisplayWidth(xctx.dc->dpy, screen); | ||||||
|  |                 scr->dim.h = DisplayHeight(xctx.dc->dpy, screen); | ||||||
|  |         } | ||||||
|  | } | ||||||
|  | // }}}
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         /*
 | ||||||
|  |          * Setup X11 stuff | ||||||
|  |          */ | ||||||
|  | void x_setup(void) | ||||||
|  | { // {{{
 | ||||||
|  | 
 | ||||||
|  |         /* initialize xctx.dc, font, keyboard, colors */ | ||||||
|  |         xctx.dc = initdc(); | ||||||
|  | 
 | ||||||
|  |         initfont(xctx.dc, settings.font); | ||||||
|  | 
 | ||||||
|  |         x_shortcut_init(&settings.close_ks); | ||||||
|  |         x_shortcut_init(&settings.close_all_ks); | ||||||
|  |         x_shortcut_init(&settings.history_ks); | ||||||
|  |         x_shortcut_init(&settings.context_ks); | ||||||
|  | 
 | ||||||
|  |         x_shortcut_grab(&settings.close_ks); | ||||||
|  |         x_shortcut_ungrab(&settings.close_ks); | ||||||
|  |         x_shortcut_grab(&settings.close_all_ks); | ||||||
|  |         x_shortcut_ungrab(&settings.close_all_ks); | ||||||
|  |         x_shortcut_grab(&settings.history_ks); | ||||||
|  |         x_shortcut_ungrab(&settings.history_ks); | ||||||
|  |         x_shortcut_grab(&settings.context_ks); | ||||||
|  |         x_shortcut_ungrab(&settings.context_ks); | ||||||
|  | 
 | ||||||
|  |         xctx.color_strings[ColFG][LOW] = settings.lowfgcolor; | ||||||
|  |         xctx.color_strings[ColFG][NORM] = settings.normfgcolor; | ||||||
|  |         xctx.color_strings[ColFG][CRIT] = settings.critfgcolor; | ||||||
|  | 
 | ||||||
|  |         xctx.color_strings[ColBG][LOW] = settings.lowbgcolor; | ||||||
|  |         xctx.color_strings[ColBG][NORM] = settings.normbgcolor; | ||||||
|  |         xctx.color_strings[ColBG][CRIT] = settings.critbgcolor; | ||||||
|  | 
 | ||||||
|  |         xctx.framec = getcolor(xctx.dc, settings.frame_color); | ||||||
|  | 
 | ||||||
|  |         if (settings.sep_color == CUSTOM) { | ||||||
|  |                 xctx.sep_custom_col = getcolor(xctx.dc, settings.sep_custom_color_str); | ||||||
|  |         } else { | ||||||
|  |                 xctx.sep_custom_col = 0; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /* parse and set xctx.geometry and monitor position */ | ||||||
|  |         if (settings.geom[0] == '-') { | ||||||
|  |                 xctx.geometry.negative_width = true; | ||||||
|  |                 settings.geom++; | ||||||
|  |         } else { | ||||||
|  |                 xctx.geometry.negative_width = false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         xctx.geometry.mask = XParseGeometry(settings.geom, | ||||||
|  |                                        &xctx.geometry.x, &xctx.geometry.y, | ||||||
|  |                                        &xctx.geometry.w, &xctx.geometry.h); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         xctx.screensaver_info = XScreenSaverAllocInfo(); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         x_win_setup(); | ||||||
|  |         x_shortcut_grab(&settings.history_ks); | ||||||
|  | } | ||||||
|  | // }}}
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // }}}
 | ||||||
|  | 
 | ||||||
|  | /* TODO comments and naming */ | ||||||
|  | // {{{ X_RENDER
 | ||||||
|  | 
 | ||||||
|  | GSList *do_word_wrap(char *text, int max_width) | ||||||
|  | { // {{{
 | ||||||
|  | 
 | ||||||
|  |         GSList *result = NULL; | ||||||
|  |         g_strstrip(text); | ||||||
|  | 
 | ||||||
|  |         if (!text || strlen(text) == 0) | ||||||
|  |                 return 0; | ||||||
|  | 
 | ||||||
|  |         char *begin = text; | ||||||
|  |         char *end = text; | ||||||
|  | 
 | ||||||
|  |         while (true) { | ||||||
|  |                 if (*end == '\0') { | ||||||
|  |                         result = g_slist_append(result, g_strdup(begin)); | ||||||
|  |                         break; | ||||||
|  |                 } | ||||||
|  |                 if (*end == '\n') { | ||||||
|  |                         *end = ' '; | ||||||
|  |                         result = g_slist_append(result, g_strndup(begin, end - begin)); | ||||||
|  |                         begin = ++end; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (settings.word_wrap && max_width > 0 && textnw(xctx.dc, begin, (end - begin) + 1) > max_width) { | ||||||
|  |                         /* find previous space */ | ||||||
|  |                         char *space = end; | ||||||
|  |                         while (space > begin && !isspace(*space)) | ||||||
|  |                                 space--; | ||||||
|  | 
 | ||||||
|  |                         if (space > begin) { | ||||||
|  |                                 end = space; | ||||||
|  |                         } | ||||||
|  |                         result = g_slist_append(result, g_strndup(begin, end - begin)); | ||||||
|  |                         begin = ++end; | ||||||
|  |                 } | ||||||
|  |                 end++; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return result; | ||||||
|  | } | ||||||
|  | // }}}
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | char *generate_final_text(notification *n) | ||||||
|  | { // {{{
 | ||||||
|  |         char *msg = g_strstrip(n->msg); | ||||||
|  |         char *buf; | ||||||
|  | 
 | ||||||
|  |         /* print dup_count and msg*/ | ||||||
|  |         if (n->dup_count > 0 && (n->actions || n->urls)) { | ||||||
|  |                 buf = g_strdup_printf("(%d%s%s) %s", | ||||||
|  |                                 n->dup_count, | ||||||
|  |                                 n->actions ? "A" : "", | ||||||
|  |                                 n->urls ? "U" : "", | ||||||
|  |                                 msg); | ||||||
|  |         } else if (n->actions || n->urls) { | ||||||
|  |                 buf = g_strdup_printf("(%s%s) %s", | ||||||
|  |                                 n->actions ? "A" : "", | ||||||
|  |                                 n->urls ? "U" : "", | ||||||
|  |                                 msg); | ||||||
|  |         } else { | ||||||
|  |                 buf = g_strdup(msg); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /* print age */ | ||||||
|  |         int hours, minutes, seconds; | ||||||
|  |         time_t t_delta = time(NULL) - n->timestamp; | ||||||
|  | 
 | ||||||
|  |         if (settings.show_age_threshold >= 0 && t_delta >= settings.show_age_threshold) { | ||||||
|  |                 hours = t_delta / 3600; | ||||||
|  |                 minutes = t_delta / 60 % 60; | ||||||
|  |                 seconds = t_delta % 60; | ||||||
|  | 
 | ||||||
|  |                 char *new_buf; | ||||||
|  |                 if (hours > 0) { | ||||||
|  |                         new_buf = g_strdup_printf("%s (%dh %dm %ds old)", buf, hours, | ||||||
|  |                                  minutes, seconds); | ||||||
|  |                 } else if (minutes > 0) { | ||||||
|  |                         new_buf = g_strdup_printf("%s (%dm %ds old)", buf, minutes, | ||||||
|  |                                  seconds); | ||||||
|  |                 } else { | ||||||
|  |                         new_buf = g_strdup_printf("%s (%ds old)", buf, seconds); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 free(buf); | ||||||
|  |                 buf = new_buf; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return buf; | ||||||
|  | } | ||||||
|  | // }}}
 | ||||||
|  | 
 | ||||||
|  | int calculate_x_offset(int line_width, int text_width) | ||||||
|  | { // {{{
 | ||||||
|  |         int leftover = line_width - text_width; | ||||||
|  |         struct timeval t; | ||||||
|  |         float pos; | ||||||
|  |         /* If the text is wider than the frame, bouncing is enabled and word_wrap disabled */ | ||||||
|  |         if (line_width < text_width && settings.bounce_freq > 0.0001 && !settings.word_wrap) { | ||||||
|  |                 gettimeofday(&t, NULL); | ||||||
|  |                 pos = | ||||||
|  |                     ((t.tv_sec % 100) * 1e6 + t.tv_usec) / (1e6 / settings.bounce_freq); | ||||||
|  |                 return (1 + sinf(2 * 3.14159 * pos)) * leftover / 2; | ||||||
|  |         } | ||||||
|  |         switch (settings.align) { | ||||||
|  |         case left: | ||||||
|  |                 return settings.frame_width + settings.h_padding; | ||||||
|  |         case center: | ||||||
|  |                 return settings.frame_width + settings.h_padding + (leftover / 2); | ||||||
|  |         case right: | ||||||
|  |                 return settings.frame_width + settings.h_padding + leftover; | ||||||
|  |         default: | ||||||
|  |                 /* this can't happen */ | ||||||
|  |                 return 0; | ||||||
|  |         } | ||||||
|  | } | ||||||
|  | // }}}
 | ||||||
|  | 
 | ||||||
|  | unsigned long calculate_foreground_color(unsigned long source_color) | ||||||
|  | { // {{{
 | ||||||
|  |         Colormap cmap = DefaultColormap(xctx.dc->dpy, DefaultScreen(xctx.dc->dpy)); | ||||||
|  |         XColor color; | ||||||
|  | 
 | ||||||
|  |         color.pixel = source_color; | ||||||
|  |         XQueryColor(xctx.dc->dpy, cmap, &color); | ||||||
|  | 
 | ||||||
|  |         int c_delta = 10000; | ||||||
|  | 
 | ||||||
|  |         /* do we need to darken or brighten the colors? */ | ||||||
|  |         int darken = (color.red + color.green + color.blue) / 3 > 65535 / 2; | ||||||
|  | 
 | ||||||
|  |         if (darken) { | ||||||
|  |                 if (color.red - c_delta < 0) | ||||||
|  |                         color.red = 0; | ||||||
|  |                 else | ||||||
|  |                         color.red -= c_delta; | ||||||
|  |                 if (color.green - c_delta < 0) | ||||||
|  |                         color.green = 0; | ||||||
|  |                 else | ||||||
|  |                         color.green -= c_delta; | ||||||
|  |                 if (color.blue - c_delta < 0) | ||||||
|  |                         color.blue = 0; | ||||||
|  |                 else | ||||||
|  |                         color.blue -= c_delta; | ||||||
|  |         } else { | ||||||
|  |                 if (color.red + c_delta > 65535) | ||||||
|  |                         color.red = 65535; | ||||||
|  |                 else | ||||||
|  |                         color.red += c_delta; | ||||||
|  |                 if (color.green + c_delta > 65535) | ||||||
|  |                         color.green = 65535; | ||||||
|  |                 else | ||||||
|  |                         color.green += c_delta; | ||||||
|  |                 if (color.blue + c_delta > 65535) | ||||||
|  |                         color.green = 65535; | ||||||
|  |                 else | ||||||
|  |                         color.green += c_delta; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         color.pixel = 0; | ||||||
|  |         XAllocColor(xctx.dc->dpy, cmap, &color); | ||||||
|  |         return color.pixel; | ||||||
|  | } | ||||||
|  | // }}}
 | ||||||
|  | 
 | ||||||
|  | int calculate_width(void) | ||||||
|  | { // {{{
 | ||||||
|  |         screen_info scr; | ||||||
|  |         x_screen_info(&scr); | ||||||
|  |         if (xctx.geometry.mask & WidthValue && xctx.geometry.w == 0) { | ||||||
|  |                 /* dynamic width */ | ||||||
|  |                 return 0; | ||||||
|  |         } else if (xctx.geometry.mask & WidthValue) { | ||||||
|  |                 /* fixed width */ | ||||||
|  |                 if (xctx.geometry.negative_width) { | ||||||
|  |                         return scr.dim.w - xctx.geometry.w; | ||||||
|  |                 } else { | ||||||
|  |                         return xctx.geometry.w; | ||||||
|  |                 } | ||||||
|  |         } else { | ||||||
|  |                 /* across the screen */ | ||||||
|  |                 return scr.dim.w; | ||||||
|  |         } | ||||||
|  | } | ||||||
|  | // }}}
 | ||||||
|  | 
 | ||||||
|  | void move_and_map(int width, int height) | ||||||
|  | { // {{{
 | ||||||
|  | 
 | ||||||
|  |         int x,y; | ||||||
|  |         screen_info scr; | ||||||
|  |         x_screen_info(&scr); | ||||||
|  |         /* calculate window position */ | ||||||
|  |         if (xctx.geometry.mask & XNegative) { | ||||||
|  |                 x = (scr.dim.x + (scr.dim.w - width)) + xctx.geometry.x; | ||||||
|  |         } else { | ||||||
|  |                 x = scr.dim.x + xctx.geometry.x; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (xctx.geometry.mask & YNegative) { | ||||||
|  |                 y = scr.dim.y + (scr.dim.h + xctx.geometry.y) - height; | ||||||
|  |         } else { | ||||||
|  |                 y = scr.dim.y + xctx.geometry.y; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /* move and map window */ | ||||||
|  |         if (x != xctx.window_dim.x || y != xctx.window_dim.y | ||||||
|  |             || width != xctx.window_dim.w || height != xctx.window_dim.h) { | ||||||
|  | 
 | ||||||
|  |                 XResizeWindow(xctx.dc->dpy, xctx.win, width, height); | ||||||
|  |                 XMoveWindow(xctx.dc->dpy, xctx.win, x, y); | ||||||
|  | 
 | ||||||
|  |                 xctx.window_dim.x = x; | ||||||
|  |                 xctx.window_dim.y = y; | ||||||
|  |                 xctx.window_dim.h = height; | ||||||
|  |                 xctx.window_dim.w = width; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         mapdc(xctx.dc, xctx.win, width, height); | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | // }}}
 | ||||||
|  | 
 | ||||||
|  | GSList *generate_render_texts(int width) | ||||||
|  | { // {{{
 | ||||||
|  |         GSList *render_texts = NULL; | ||||||
|  | 
 | ||||||
|  |         for (GList *iter = g_queue_peek_head_link(displayed); iter; iter = iter->next) { | ||||||
|  |                 render_text *rt = g_malloc(sizeof(render_text)); | ||||||
|  | 
 | ||||||
|  |                 rt->colors = ((notification*)iter->data)->colors; | ||||||
|  |                 char *text = generate_final_text(iter->data); | ||||||
|  |                 rt->lines = do_word_wrap(text, width); | ||||||
|  |                 free(text); | ||||||
|  |                 render_texts = g_slist_append(render_texts, rt); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /* add (x more) */ | ||||||
|  |         if (settings.indicate_hidden && queue->length > 0) { | ||||||
|  |                 if (xctx.geometry.h != 1) { | ||||||
|  |                         render_text *rt = g_malloc(sizeof(render_text)); | ||||||
|  |                         rt->colors = ((render_text *) g_slist_last(render_texts)->data)->colors; | ||||||
|  |                         rt->lines = g_slist_append(NULL, g_strdup_printf("%d more)", queue->length)); | ||||||
|  |                         render_texts = g_slist_append(render_texts, rt); | ||||||
|  |                 } else { | ||||||
|  |                         GSList *last_lines = ((render_text *) g_slist_last(render_texts)->data)->lines; | ||||||
|  |                         GSList *last_line = g_slist_last(last_lines); | ||||||
|  |                         char *old = last_line->data; | ||||||
|  |                         char *new = g_strdup_printf("%s (%d more)", old, queue->length); | ||||||
|  |                         free(old); | ||||||
|  |                         last_line->data = new; | ||||||
|  |                 } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return render_texts; | ||||||
|  | } | ||||||
|  | // }}}
 | ||||||
|  | 
 | ||||||
|  | void free_render_text(void *data) { | ||||||
|  |         g_slist_free_full(((render_text *) data)->lines, g_free); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void free_render_texts(GSList *texts) { | ||||||
|  |         g_slist_free_full(texts, free_render_text); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // }}}
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // {{{ X_WIN
 | ||||||
|  | 
 | ||||||
|  | void x_win_draw(void) | ||||||
|  | { // {{{
 | ||||||
|  | 
 | ||||||
|  |         int outer_width = calculate_width(); | ||||||
|  |         screen_info scr; | ||||||
|  |         x_screen_info(&scr); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         settings.line_height = MAX(settings.line_height, xctx.font_h); | ||||||
|  | 
 | ||||||
|  |         int width; | ||||||
|  |         if (outer_width == 0) | ||||||
|  |                 width = 0; | ||||||
|  |         else | ||||||
|  |                 width = outer_width - (2 * settings.frame_width) - (2 * settings.h_padding); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         GSList *texts = generate_render_texts(width); | ||||||
|  |         int line_count = 0; | ||||||
|  |         for (GSList *iter = texts; iter; iter = iter->next) { | ||||||
|  |                 render_text *tmp = iter->data; | ||||||
|  |                 line_count += g_slist_length(tmp->lines); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /* if we have a dynamic width, calculate the actual width */ | ||||||
|  |         if (width == 0) { | ||||||
|  |                 for (GSList *iter = texts; iter; iter = iter->next) { | ||||||
|  |                         GSList *lines = ((render_text *) iter->data)->lines; | ||||||
|  |                         for (GSList *iiter = lines; iiter; iiter = iiter->next) | ||||||
|  |                                 width = MAX(width, textw(xctx.dc, iiter->data)); | ||||||
|  |                 } | ||||||
|  |                 outer_width = width + (2 * settings.frame_width) + (2 * settings.h_padding); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /* resize xctx.dc to correct width */ | ||||||
|  | 
 | ||||||
|  |         int height = (line_count * settings.line_height) | ||||||
|  |                    + displayed->length * 2 * settings.padding | ||||||
|  |                    + ((settings.indicate_hidden && queue->length > 0 && xctx.geometry.h != 1) ? 2 * settings.padding : 0) | ||||||
|  |                    + (settings.separator_height * (displayed->length - 1)) | ||||||
|  |                    + (2 * settings.frame_width); | ||||||
|  | 
 | ||||||
|  |         resizedc(xctx.dc, outer_width, height); | ||||||
|  | 
 | ||||||
|  |         /* draw frame
 | ||||||
|  |          * this draws a big box in the frame color which get filled with | ||||||
|  |          * smaller boxes of the notification colors | ||||||
|  |          */ | ||||||
|  |         xctx.dc->y = 0; | ||||||
|  |         xctx.dc->x = 0; | ||||||
|  |         if (settings.frame_width > 0) { | ||||||
|  |                 drawrect(xctx.dc, 0, 0, outer_width, height, true, xctx.framec); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         xctx.dc->y = settings.frame_width; | ||||||
|  |         xctx.dc->x = settings.frame_width; | ||||||
|  | 
 | ||||||
|  |         for (GSList *iter = texts; iter; iter = iter->next) { | ||||||
|  | 
 | ||||||
|  |                 render_text *cur = iter->data; | ||||||
|  |                 ColorSet *colors = cur->colors; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |                 int line_count = 0; | ||||||
|  |                 bool first_line = true; | ||||||
|  |                 for (GSList *iiter = cur->lines; iiter; iiter = iiter->next) { | ||||||
|  |                         char *line = iiter->data; | ||||||
|  |                         line_count++; | ||||||
|  | 
 | ||||||
|  |                         int pad = 0; | ||||||
|  |                         bool last_line = iiter->next == NULL; | ||||||
|  | 
 | ||||||
|  |                         if (first_line && last_line) | ||||||
|  |                                 pad = 2*settings.padding; | ||||||
|  |                         else if (first_line || last_line) | ||||||
|  |                                 pad = settings.padding; | ||||||
|  | 
 | ||||||
|  |                         xctx.dc->x = settings.frame_width; | ||||||
|  | 
 | ||||||
|  |                         /* draw background */ | ||||||
|  |                         drawrect(xctx.dc, 0, 0, width + (2*settings.h_padding), pad +  settings.line_height, true, colors->BG); | ||||||
|  | 
 | ||||||
|  |                         /* draw text */ | ||||||
|  |                         xctx.dc->x = calculate_x_offset(width, textw(xctx.dc, line)); | ||||||
|  | 
 | ||||||
|  |                         xctx.dc->y += ((settings.line_height - xctx.font_h) / 2); | ||||||
|  |                         xctx.dc->y += first_line ? settings.padding : 0; | ||||||
|  | 
 | ||||||
|  |                         drawtextn(xctx.dc, line, strlen(line), colors); | ||||||
|  | 
 | ||||||
|  |                         xctx.dc->y += settings.line_height - ((settings.line_height - xctx.font_h) / 2); | ||||||
|  |                         xctx.dc->y += last_line ? settings.padding : 0; | ||||||
|  | 
 | ||||||
|  |                         first_line = false; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 /* draw separator */ | ||||||
|  |                 if (settings.separator_height > 0 && iter->next) { | ||||||
|  |                         xctx.dc->x = settings.frame_width; | ||||||
|  |                         double color; | ||||||
|  |                         if (settings.sep_color == AUTO) | ||||||
|  |                                 color = calculate_foreground_color(colors->BG); | ||||||
|  |                         else if (settings.sep_color == FOREGROUND) | ||||||
|  |                                 color = colors->FG; | ||||||
|  |                         else if (settings.sep_color == FRAME) | ||||||
|  |                                 color = xctx.framec; | ||||||
|  |                         else { | ||||||
|  |                                 /* CUSTOM */ | ||||||
|  |                                 color = xctx.sep_custom_col; | ||||||
|  |                         } | ||||||
|  |                         drawrect(xctx.dc, 0, 0, width + (2*settings.h_padding), settings.separator_height, true, color); | ||||||
|  |                         xctx.dc->y += settings.separator_height; | ||||||
|  |                 } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         move_and_map(outer_width, height); | ||||||
|  | 
 | ||||||
|  |         free_render_texts(texts); | ||||||
|  | } | ||||||
|  | // }}}
 | ||||||
|  | 
 | ||||||
|  |         /*
 | ||||||
|  |          * Setup the window | ||||||
|  |          */ | ||||||
|  | void x_win_setup(void) | ||||||
|  | { // {{{
 | ||||||
|  | 
 | ||||||
|  |         Window root; | ||||||
|  |         XSetWindowAttributes wa; | ||||||
|  | 
 | ||||||
|  |         xctx.window_dim.x = 0; | ||||||
|  |         xctx.window_dim.y = 0; | ||||||
|  |         xctx.window_dim.w = 0; | ||||||
|  |         xctx.window_dim.h = 0; | ||||||
|  | 
 | ||||||
|  |         root = RootWindow(xctx.dc->dpy, DefaultScreen(xctx.dc->dpy)); | ||||||
|  |         xctx.utf8 = XInternAtom(xctx.dc->dpy, "UTF8_STRING", false); | ||||||
|  |         xctx.font_h = xctx.dc->font.height + FONT_HEIGHT_BORDER; | ||||||
|  | 
 | ||||||
|  |         wa.override_redirect = true; | ||||||
|  |         wa.background_pixmap = ParentRelative; | ||||||
|  |         wa.event_mask = | ||||||
|  |             ExposureMask | KeyPressMask | VisibilityChangeMask | | ||||||
|  |             ButtonPressMask; | ||||||
|  | 
 | ||||||
|  |         screen_info scr; | ||||||
|  |         x_screen_info(&scr); | ||||||
|  |         xctx.win = | ||||||
|  |             XCreateWindow(xctx.dc->dpy, root, scr.dim.x, scr.dim.y, scr.dim.w, | ||||||
|  |                           xctx.font_h, 0, DefaultDepth(xctx.dc->dpy, | ||||||
|  |                                                   DefaultScreen(xctx.dc->dpy)), | ||||||
|  |                           CopyFromParent, DefaultVisual(xctx.dc->dpy, | ||||||
|  |                                                         DefaultScreen(xctx.dc->dpy)), | ||||||
|  |                           CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); | ||||||
|  |         settings.transparency = settings.transparency > 100 ? 100 : settings.transparency; | ||||||
|  |         setopacity(xctx.dc, xctx.win, | ||||||
|  |                    (unsigned long)((100 - settings.transparency) * (0xffffffff / 100))); | ||||||
|  | } | ||||||
|  | // }}}
 | ||||||
|  | 
 | ||||||
|  |         /*
 | ||||||
|  |          * Show the window and grab shortcuts. | ||||||
|  |          */ | ||||||
|  | void x_win_show(void) | ||||||
|  | { // {{{
 | ||||||
|  |         /* window is already mapped or there's nothing to show */ | ||||||
|  |         if (xctx.visible || g_queue_is_empty(displayed)) { | ||||||
|  |                 return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         x_shortcut_grab(&settings.close_ks); | ||||||
|  |         x_shortcut_grab(&settings.close_all_ks); | ||||||
|  |         x_shortcut_grab(&settings.context_ks); | ||||||
|  | 
 | ||||||
|  |         x_shortcut_setup_error_handler(); | ||||||
|  |         XGrabButton(xctx.dc->dpy, AnyButton, AnyModifier, xctx.win, false, | ||||||
|  |                     BUTTONMASK, GrabModeAsync, GrabModeSync, None, None); | ||||||
|  |         if (x_shortcut_tear_down_error_handler()) { | ||||||
|  |                 fprintf(stderr, "Unable to grab mouse button(s)\n"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         XMapRaised(xctx.dc->dpy, xctx.win); | ||||||
|  |         xctx.visible = true; | ||||||
|  | } | ||||||
|  | // }}}
 | ||||||
|  | 
 | ||||||
|  |         /*
 | ||||||
|  |          * Hide the window and ungrab unused keyboard_shortcuts | ||||||
|  |          */ | ||||||
|  | void x_win_hide() | ||||||
|  | { // {{{
 | ||||||
|  |         x_shortcut_ungrab(&settings.close_ks); | ||||||
|  |         x_shortcut_ungrab(&settings.close_all_ks); | ||||||
|  |         x_shortcut_ungrab(&settings.context_ks); | ||||||
|  | 
 | ||||||
|  |         XUngrabButton(xctx.dc->dpy, AnyButton, AnyModifier, xctx.win); | ||||||
|  |         XUnmapWindow(xctx.dc->dpy, xctx.win); | ||||||
|  |         XFlush(xctx.dc->dpy); | ||||||
|  |         xctx.visible = false; | ||||||
|  | } | ||||||
|  | // }}}
 | ||||||
|  | 
 | ||||||
|  | // }}}
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // {{{ X_SHORTCUT
 | ||||||
|  | 
 | ||||||
|  |         /*
 | ||||||
|  |          * Parse a string into a modifier mask. | ||||||
|  |          */ | ||||||
|  | KeySym x_shortcut_string_to_mask(const char *str) | ||||||
|  | { // {{{
 | ||||||
|  |         if (!strcmp(str, "ctrl")) { | ||||||
|  |                 return ControlMask; | ||||||
|  |         } else if (!strcmp(str, "mod4")) { | ||||||
|  |                 return Mod4Mask; | ||||||
|  |         } else if (!strcmp(str, "mod3")) { | ||||||
|  |                 return Mod3Mask; | ||||||
|  |         } else if (!strcmp(str, "mod2")) { | ||||||
|  |                 return Mod2Mask; | ||||||
|  |         } else if (!strcmp(str, "mod1")) { | ||||||
|  |                 return Mod1Mask; | ||||||
|  |         } else if (!strcmp(str, "shift")) { | ||||||
|  |                 return ShiftMask; | ||||||
|  |         } else { | ||||||
|  |                 fprintf(stderr, "Warning: Unknown Modifier: %s\n", str); | ||||||
|  |                 return 0; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | // }}}
 | ||||||
|  | 
 | ||||||
|  |         /*
 | ||||||
|  |          * Error handler for grabbing mouse and keyboard errors. | ||||||
|  |          */ | ||||||
|  | static int GrabXErrorHandler(Display * display, XErrorEvent * e) | ||||||
|  | { // {{{
 | ||||||
|  |         dunst_grab_errored = true; | ||||||
|  |         char err_buf[BUFSIZ]; | ||||||
|  |         XGetErrorText(display, e->error_code, err_buf, BUFSIZ); | ||||||
|  |         fputs(err_buf, stderr); | ||||||
|  |         fputs("\n", stderr); | ||||||
|  | 
 | ||||||
|  |         if (e->error_code != BadAccess) { | ||||||
|  |                 exit(EXIT_FAILURE); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return 0; | ||||||
|  | } | ||||||
|  | // }}}
 | ||||||
|  | 
 | ||||||
|  |         /*
 | ||||||
|  |          * Setup the Error handler. | ||||||
|  |          */ | ||||||
|  | static void x_shortcut_setup_error_handler(void) | ||||||
|  | { // {{{
 | ||||||
|  |         dunst_grab_errored = false; | ||||||
|  | 
 | ||||||
|  |         XFlush(xctx.dc->dpy); | ||||||
|  |         XSetErrorHandler(GrabXErrorHandler); | ||||||
|  | } | ||||||
|  | // }}}
 | ||||||
|  | 
 | ||||||
|  |         /*
 | ||||||
|  |          * Tear down the Error handler. | ||||||
|  |          */ | ||||||
|  | static int x_shortcut_tear_down_error_handler(void) | ||||||
|  | { // {{{
 | ||||||
|  |         XFlush(xctx.dc->dpy); | ||||||
|  |         XSync(xctx.dc->dpy, false); | ||||||
|  |         XSetErrorHandler(NULL); | ||||||
|  |         return dunst_grab_errored; | ||||||
|  | } | ||||||
|  | // }}}
 | ||||||
|  | 
 | ||||||
|  |         /*
 | ||||||
|  |          * Grab the given keyboard shortcut. | ||||||
|  |          */ | ||||||
|  | int x_shortcut_grab(keyboard_shortcut *ks) | ||||||
|  | { // {{{
 | ||||||
|  |         if (!ks->is_valid) | ||||||
|  |                 return 1; | ||||||
|  |         Window root; | ||||||
|  |         root = RootWindow(xctx.dc->dpy, DefaultScreen(xctx.dc->dpy)); | ||||||
|  | 
 | ||||||
|  |         x_shortcut_setup_error_handler(); | ||||||
|  | 
 | ||||||
|  |         if (ks->is_valid) | ||||||
|  |                 XGrabKey(xctx.dc->dpy, ks->code, ks->mask, root, | ||||||
|  |                          true, GrabModeAsync, GrabModeAsync); | ||||||
|  | 
 | ||||||
|  |         if (x_shortcut_tear_down_error_handler()) { | ||||||
|  |                 fprintf(stderr, "Unable to grab key \"%s\"\n", ks->str); | ||||||
|  |                 ks->is_valid = false; | ||||||
|  |                 return 1; | ||||||
|  |         } | ||||||
|  |         return 0; | ||||||
|  | } | ||||||
|  | // }}}
 | ||||||
|  | 
 | ||||||
|  |         /*
 | ||||||
|  |          * Ungrab the given keyboard shortcut. | ||||||
|  |          */ | ||||||
|  | void x_shortcut_ungrab(keyboard_shortcut *ks) | ||||||
|  | { // {{{
 | ||||||
|  |         Window root; | ||||||
|  |         root = RootWindow(xctx.dc->dpy, DefaultScreen(xctx.dc->dpy)); | ||||||
|  |         if (ks->is_valid) | ||||||
|  |                 XUngrabKey(xctx.dc->dpy, ks->code, ks->mask, root); | ||||||
|  | } | ||||||
|  | // }}}
 | ||||||
|  | 
 | ||||||
|  |         /*
 | ||||||
|  |          * Initialize the keyboard shortcut. | ||||||
|  |          */ | ||||||
|  | void x_shortcut_init(keyboard_shortcut *ks) | ||||||
|  | { // {{{
 | ||||||
|  |         if (ks == NULL || ks->str == NULL) | ||||||
|  |                 return; | ||||||
|  | 
 | ||||||
|  |         if (!strcmp(ks->str, "none") || (!strcmp(ks->str, ""))) { | ||||||
|  |                 ks->is_valid = false; | ||||||
|  |                 return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         char *str = g_strdup(ks->str); | ||||||
|  |         char *str_begin = str; | ||||||
|  | 
 | ||||||
|  |         if (str == NULL) | ||||||
|  |                 die("Unable to allocate memory", EXIT_FAILURE); | ||||||
|  | 
 | ||||||
|  |         while (strstr(str, "+")) { | ||||||
|  |                 char *mod = str; | ||||||
|  |                 while (*str != '+') | ||||||
|  |                         str++; | ||||||
|  |                 *str = '\0'; | ||||||
|  |                 str++; | ||||||
|  |                 g_strchomp(mod); | ||||||
|  |                 ks->mask = ks->mask | x_shortcut_string_to_mask(mod); | ||||||
|  |         } | ||||||
|  |         g_strstrip(str); | ||||||
|  | 
 | ||||||
|  |         ks->sym = XStringToKeysym(str); | ||||||
|  |         /* find matching keycode for ks->sym */ | ||||||
|  |         int min_keysym, max_keysym; | ||||||
|  |         XDisplayKeycodes(xctx.dc->dpy, &min_keysym, &max_keysym); | ||||||
|  | 
 | ||||||
|  |         ks->code = NoSymbol; | ||||||
|  | 
 | ||||||
|  |         for (int i = min_keysym; i <= max_keysym; i++) { | ||||||
|  |                 if (XkbKeycodeToKeysym(xctx.dc->dpy, i, 0, 0) == ks->sym | ||||||
|  |                     || XkbKeycodeToKeysym(xctx.dc->dpy, i, 0, 1) == ks->sym) { | ||||||
|  |                         ks->code = i; | ||||||
|  |                         break; | ||||||
|  |                 } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (ks->sym == NoSymbol || ks->code == NoSymbol) { | ||||||
|  |                 fprintf(stderr, "Warning: Unknown keyboard shortcut: %s\n", | ||||||
|  |                         ks->str); | ||||||
|  |                 ks->is_valid = false; | ||||||
|  |         } else { | ||||||
|  |                 ks->is_valid = true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         free(str_begin); | ||||||
|  | } | ||||||
|  | // }}}
 | ||||||
|  | 
 | ||||||
|  | // }}}
 | ||||||
|  | // }}}
 | ||||||
| /* vim: set ts=8 sw=8 tw=0: */ | /* vim: set ts=8 sw=8 tw=0: */ | ||||||
|  | |||||||
							
								
								
									
										28
									
								
								x.h
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								x.h
									
									
									
									
									
								
							| @ -46,6 +46,12 @@ DEALINGS IN THE SOFTWARE. | |||||||
| 
 | 
 | ||||||
| #include <X11/Xft/Xft.h> | #include <X11/Xft/Xft.h> | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | #define BUTTONMASK              (ButtonPressMask|ButtonReleaseMask) | ||||||
|  | #define FONT_HEIGHT_BORDER 2 | ||||||
|  | #define DEFFONT "Monospace-11" | ||||||
|  | #define INRECT(x,y,rx,ry,rw,rh) ((x) >= (rx) && (x) < (rx)+(rw) && (y) >= (ry) && (y) < (ry)+(rh)) | ||||||
|  | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|         int x, y, w, h; |         int x, y, w, h; | ||||||
|         bool invert; |         bool invert; | ||||||
| @ -131,5 +137,27 @@ void resizedc(DC * dc, unsigned int w, unsigned int h); | |||||||
| int textnw(DC * dc, const char *text, size_t len); | int textnw(DC * dc, const char *text, size_t len); | ||||||
| int textw(DC * dc, const char *text); | int textw(DC * dc, const char *text); | ||||||
| 
 | 
 | ||||||
|  | /* window */ | ||||||
|  | void x_win_draw(void); | ||||||
|  | void x_win_hide(void); | ||||||
|  | void x_win_show(void); | ||||||
|  | void x_win_setup(void); | ||||||
|  | 
 | ||||||
|  | /* shortcut */ | ||||||
|  | void x_shortcut_init(keyboard_shortcut *shortcut); | ||||||
|  | void x_shortcut_ungrab(keyboard_shortcut *ks); | ||||||
|  | int x_shortcut_grab(keyboard_shortcut *ks); | ||||||
|  | KeySym x_shortcut_string_to_mask(const char *str); | ||||||
|  | 
 | ||||||
|  | /* X misc */ | ||||||
|  | void x_handle_click(XEvent ev); | ||||||
|  | void x_screen_info(screen_info *scr); | ||||||
|  | bool x_is_idle(void); | ||||||
|  | void x_setup(void); | ||||||
|  | 
 | ||||||
|  | gboolean x_mainloop_fd_dispatch(GSource *source, GSourceFunc callback, gpointer user_data); | ||||||
|  | gboolean x_mainloop_fd_check(GSource *source); | ||||||
|  | gboolean x_mainloop_fd_prepare(GSource *source, gint *timeout); | ||||||
|  | 
 | ||||||
| #endif | #endif | ||||||
| /* vim: set ts=8 sw=8 tw=0: */ | /* vim: set ts=8 sw=8 tw=0: */ | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Sascha Kruse
						Sascha Kruse