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); | ||||
| 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: */ | ||||
|  | ||||
							
								
								
									
										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. | ||||
| 
 | ||||
| */ | ||||
| 
 | ||||
| #include <math.h> | ||||
| #include <sys/time.h> | ||||
| #include <ctype.h> | ||||
| #include <assert.h> | ||||
| #include <locale.h> | ||||
| #include <stdarg.h> | ||||
| #include <stdio.h> | ||||
| @ -41,8 +44,16 @@ DEALINGS IN THE SOFTWARE. | ||||
| #include <X11/Xatom.h> | ||||
| 
 | ||||
| #include "x.h" | ||||
| #include "utils.h" | ||||
| #include "dunst.h" | ||||
| #include "settings.h" | ||||
| #include "notification.h" | ||||
| 
 | ||||
| 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 | ||||
| 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; | ||||
| } | ||||
| 
 | ||||
| // {{{ 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: */ | ||||
|  | ||||
							
								
								
									
										28
									
								
								x.h
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								x.h
									
									
									
									
									
								
							| @ -46,6 +46,12 @@ DEALINGS IN THE SOFTWARE. | ||||
| 
 | ||||
| #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 { | ||||
|         int x, y, w, h; | ||||
|         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 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 | ||||
| /* vim: set ts=8 sw=8 tw=0: */ | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Sascha Kruse
						Sascha Kruse