Compare commits
	
		
			No commits in common. "172ceffa9ec6722076074348c21d7069212a112e" and "179e0a123bb552c7186913e2473b344079dcc3bc" have entirely different histories.
		
	
	
		
			172ceffa9e
			...
			179e0a123b
		
	
		
							
								
								
									
										2
									
								
								NEWS
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								NEWS
									
									
									
									
									
								
							| @ -6,8 +6,6 @@ Unreleased | |||||||
| 
 | 
 | ||||||
|  * Added a "z" binding to center the view on the selected item |  * Added a "z" binding to center the view on the selected item | ||||||
| 
 | 
 | ||||||
|  * Made it possible to adjust the spectrum analyzer's FPS limit |  | ||||||
| 
 |  | ||||||
|  * Fixed possibility of connection timeouts with PulseAudio integration |  * Fixed possibility of connection timeouts with PulseAudio integration | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										423
									
								
								nncmpp.c
									
									
									
									
									
								
							
							
						
						
									
										423
									
								
								nncmpp.c
									
									
									
									
									
								
							| @ -109,7 +109,6 @@ enum | |||||||
| 
 | 
 | ||||||
| // Elementary port of the TUI to X11.
 | // Elementary port of the TUI to X11.
 | ||||||
| #ifdef WITH_X11 | #ifdef WITH_X11 | ||||||
| #include <X11/Xatom.h> |  | ||||||
| #include <X11/Xlib.h> | #include <X11/Xlib.h> | ||||||
| #include <X11/keysym.h> | #include <X11/keysym.h> | ||||||
| #include <X11/XKBlib.h> | #include <X11/XKBlib.h> | ||||||
| @ -742,8 +741,7 @@ spectrum_sample (struct spectrum *s) | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool | static bool | ||||||
| spectrum_init (struct spectrum *s, char *format, int bars, int fps, | spectrum_init (struct spectrum *s, char *format, int bars, struct error **e) | ||||||
| 	struct error **e) |  | ||||||
| { | { | ||||||
| 	errno = 0; | 	errno = 0; | ||||||
| 
 | 
 | ||||||
| @ -819,7 +817,8 @@ spectrum_init (struct spectrum *s, char *format, int bars, int fps, | |||||||
| 		s->top_bins[bar] = MIN (top_bin, used_bins); | 		s->top_bins[bar] = MIN (top_bin, used_bins); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	s->samples = s->sampling_rate / s->bins * 2 / MAX (fps, 1); | 	// Limit updates to 30 times per second to limit CPU load
 | ||||||
|  | 	s->samples = s->sampling_rate / s->bins * 2 / 30; | ||||||
| 	if (s->samples < 1) | 	if (s->samples < 1) | ||||||
| 		s->samples = 1; | 		s->samples = 1; | ||||||
| 
 | 
 | ||||||
| @ -1153,9 +1152,6 @@ struct widget; | |||||||
| /// Draw a widget on the window
 | /// Draw a widget on the window
 | ||||||
| typedef void (*widget_render_fn) (struct widget *self); | typedef void (*widget_render_fn) (struct widget *self); | ||||||
| 
 | 
 | ||||||
| /// Extract the contents of container widgets
 |  | ||||||
| typedef struct widget *(*widget_sublayout_fn) (struct widget *self); |  | ||||||
| 
 |  | ||||||
| /// A minimal abstraction appropriate for both TUI and GUI widgets.
 | /// A minimal abstraction appropriate for both TUI and GUI widgets.
 | ||||||
| /// Units for the widget's region are frontend-specific.
 | /// Units for the widget's region are frontend-specific.
 | ||||||
| /// Having this as a linked list simplifies layouting and memory management.
 | /// Having this as a linked list simplifies layouting and memory management.
 | ||||||
| @ -1169,7 +1165,6 @@ struct widget | |||||||
| 	int height;                         ///< Height, initialized by UI methods
 | 	int height;                         ///< Height, initialized by UI methods
 | ||||||
| 
 | 
 | ||||||
| 	widget_render_fn on_render;         ///< Render callback
 | 	widget_render_fn on_render;         ///< Render callback
 | ||||||
| 	widget_sublayout_fn on_sublayout;   ///< Optional sublayout callback
 |  | ||||||
| 	chtype attrs;                       ///< Rendition, in Curses terms
 | 	chtype attrs;                       ///< Rendition, in Curses terms
 | ||||||
| 
 | 
 | ||||||
| 	short id;                           ///< Post-layouting identification
 | 	short id;                           ///< Post-layouting identification
 | ||||||
| @ -1362,12 +1357,10 @@ static struct app_context | |||||||
| 	int xkb_base_event_code;            ///< Xkb base event code
 | 	int xkb_base_event_code;            ///< Xkb base event code
 | ||||||
| 	Window x11_window;                  ///< Application window
 | 	Window x11_window;                  ///< Application window
 | ||||||
| 	Pixmap x11_pixmap;                  ///< Off-screen bitmap
 | 	Pixmap x11_pixmap;                  ///< Off-screen bitmap
 | ||||||
| 	Region x11_clip;                    ///< Invalidated region
 |  | ||||||
| 	Picture x11_pixmap_picture;         ///< XRender wrap for x11_pixmap
 | 	Picture x11_pixmap_picture;         ///< XRender wrap for x11_pixmap
 | ||||||
| 	XftDraw *xft_draw;                  ///< Xft rendering context
 | 	XftDraw *xft_draw;                  ///< Xft rendering context
 | ||||||
| 	XftFont *xft_regular;               ///< Regular font
 | 	XftFont *xft_regular;               ///< Regular font
 | ||||||
| 	XftFont *xft_bold;                  ///< Bold font
 | 	XftFont *xft_bold;                  ///< Bold font
 | ||||||
| 	char *x11_selection;                ///< CLIPBOARD selection
 |  | ||||||
| 
 | 
 | ||||||
| 	XRenderColor x_fg[ATTRIBUTE_COUNT]; ///< Foreground per attribute
 | 	XRenderColor x_fg[ATTRIBUTE_COUNT]; ///< Foreground per attribute
 | ||||||
| 	XRenderColor x_bg[ATTRIBUTE_COUNT]; ///< Background per attribute
 | 	XRenderColor x_bg[ATTRIBUTE_COUNT]; ///< Background per attribute
 | ||||||
| @ -1467,10 +1460,6 @@ static struct config_schema g_config_settings[] = | |||||||
| 	  .comment   = "Number of computed audio spectrum bars", | 	  .comment   = "Number of computed audio spectrum bars", | ||||||
| 	  .type      = CONFIG_ITEM_INTEGER, | 	  .type      = CONFIG_ITEM_INTEGER, | ||||||
| 	  .default_  = "8" }, | 	  .default_  = "8" }, | ||||||
| 	{ .name      = "spectrum_fps", |  | ||||||
| 	  .comment   = "Maximum frames per second, affects CPU usage", |  | ||||||
| 	  .type      = CONFIG_ITEM_INTEGER, |  | ||||||
| 	  .default_  = "30" }, |  | ||||||
| #endif  // WITH_FFTW
 | #endif  // WITH_FFTW
 | ||||||
| 
 | 
 | ||||||
| #ifdef WITH_PULSE | #ifdef WITH_PULSE | ||||||
| @ -1742,14 +1731,14 @@ app_invalidate (void) | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void | static void | ||||||
| app_flush_layout_to (struct layout *l, int width, struct layout *dest) | app_flush_layout (struct layout *l) | ||||||
| { | { | ||||||
| 	hard_assert (l != NULL && l->head != NULL); | 	hard_assert (l != NULL && l->head != NULL); | ||||||
| 	widget_redistribute (l->head, width); | 	widget_redistribute (l->head, g.ui_width); | ||||||
| 
 | 
 | ||||||
| 	struct widget *last = dest->tail; | 	struct widget *last = g.widgets.tail; | ||||||
| 	if (!last) | 	if (!last) | ||||||
| 		*dest = *l; | 		g.widgets = *l; | ||||||
| 	else | 	else | ||||||
| 	{ | 	{ | ||||||
| 		// Assuming there is no unclaimed vertical space.
 | 		// Assuming there is no unclaimed vertical space.
 | ||||||
| @ -1758,16 +1747,10 @@ app_flush_layout_to (struct layout *l, int width, struct layout *dest) | |||||||
| 
 | 
 | ||||||
| 		last->next = l->head; | 		last->next = l->head; | ||||||
| 		l->head->prev = last; | 		l->head->prev = last; | ||||||
| 		dest->tail = l->tail; | 		g.widgets.tail = l->tail; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void |  | ||||||
| app_flush_layout (struct layout *l) |  | ||||||
| { |  | ||||||
| 	app_flush_layout_to (l, g.ui_width, &g.widgets); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static struct widget * | static struct widget * | ||||||
| app_push (struct layout *l, struct widget *w) | app_push (struct layout *l, struct widget *w) | ||||||
| { | { | ||||||
| @ -2053,7 +2036,7 @@ app_compute_scrollbar (struct tab *tab, long visible, long s) | |||||||
| 	return (struct scrollbar) { length, offset }; | 	return (struct scrollbar) { length, offset }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static struct layout | static struct widget * | ||||||
| app_layout_row (struct tab *tab, int item_index) | app_layout_row (struct tab *tab, int item_index) | ||||||
| { | { | ||||||
| 	int row_attrs = (item_index & 1) ? APP_ATTR (ODD) : APP_ATTR (EVEN); | 	int row_attrs = (item_index & 1) ? APP_ATTR (ODD) : APP_ATTR (EVEN); | ||||||
| @ -2087,30 +2070,6 @@ app_layout_row (struct tab *tab, int item_index) | |||||||
| 		else | 		else | ||||||
| 			*attrs |=  row_attrs; | 			*attrs |=  row_attrs; | ||||||
| 	} | 	} | ||||||
| 	return l; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // XXX: This isn't a very clean design, in that part of layouting
 |  | ||||||
| //   is done during the rendering stage.
 |  | ||||||
| static struct widget * |  | ||||||
| app_sublayout_list (struct widget *list) |  | ||||||
| { |  | ||||||
| 	struct tab *tab = g.active_tab; |  | ||||||
| 	int to_show = MIN ((int) tab->item_count - tab->item_top, |  | ||||||
| 		list->height / g.ui_vunit); |  | ||||||
| 
 |  | ||||||
| 	struct layout l = {}; |  | ||||||
| 	for (int row = 0; row < to_show; row++) |  | ||||||
| 	{ |  | ||||||
| 		int item_index = tab->item_top + row; |  | ||||||
| 		struct layout subl = app_layout_row (tab, item_index); |  | ||||||
| 		app_flush_layout_to (&subl, list->width, &l); |  | ||||||
| 	} |  | ||||||
| 	LIST_FOR_EACH (struct widget, w, l.head) |  | ||||||
| 	{ |  | ||||||
| 		w->x += list->x; |  | ||||||
| 		w->y += list->y; |  | ||||||
| 	} |  | ||||||
| 	return l.head; | 	return l.head; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -2136,46 +2095,32 @@ app_layout_view (void) | |||||||
| 	app_flush_layout (&l); | 	app_flush_layout (&l); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void | static char * | ||||||
| app_layout_mpd_status_playlist (struct layout *l, chtype attrs) | app_mpd_status_playlist (void) | ||||||
| { | { | ||||||
| 	char *songs = (g.playlist.len == 1) | 	struct str stats = str_make (); | ||||||
| 		? xstrdup_printf ("1 song") | 	if (g.playlist.len == 1) | ||||||
| 		: xstrdup_printf ("%zu songs", g.playlist.len); | 		str_append_printf (&stats, "1 song "); | ||||||
| 	app_push (l, g.ui->label (attrs, songs)); | 	else | ||||||
| 	free (songs); | 		str_append_printf (&stats, "%zu songs ", g.playlist.len); | ||||||
| 
 | 
 | ||||||
| 	int hours   = g.playlist_time / 3600; | 	int hours   = g.playlist_time / 3600; | ||||||
| 	int minutes = g.playlist_time % 3600 / 60; | 	int minutes = g.playlist_time % 3600 / 60; | ||||||
| 	if (hours || minutes) | 	if (hours || minutes) | ||||||
| 	{ | 	{ | ||||||
| 		struct str length = str_make (); | 		str_append_c (&stats, ' '); | ||||||
|  | 
 | ||||||
| 		if (hours == 1) | 		if (hours == 1) | ||||||
| 			str_append_printf (&length, " 1 hour"); | 			str_append_printf (&stats, " 1 hour"); | ||||||
| 		else if (hours) | 		else if (hours) | ||||||
| 			str_append_printf (&length, " %d hours", hours); | 			str_append_printf (&stats, " %d hours", hours); | ||||||
| 
 | 
 | ||||||
| 		if (minutes == 1) | 		if (minutes == 1) | ||||||
| 			str_append_printf (&length, " 1 minute"); | 			str_append_printf (&stats, " 1 minute"); | ||||||
| 		else if (minutes) | 		else if (minutes) | ||||||
| 			str_append_printf (&length, " %d minutes", minutes); | 			str_append_printf (&stats, " %d minutes", minutes); | ||||||
| 
 |  | ||||||
| 		app_push (l, g.ui->padding (attrs, 1, 1)); |  | ||||||
| 		app_push (l, g.ui->label (attrs, length.str + 1)); |  | ||||||
| 		str_free (&length); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	const char *task = NULL; |  | ||||||
| 	if (g.poller_curl.registered) |  | ||||||
| 		task = "Downloading..."; |  | ||||||
| 	else if (str_map_find (&g.playback_info, "updating_db")) |  | ||||||
| 		task = "Updating database..."; |  | ||||||
| 
 |  | ||||||
| 	if (task) |  | ||||||
| 	{ |  | ||||||
| 		app_push (l, g.ui->padding (attrs, 1, 1)); |  | ||||||
| 		app_push (l, g.ui->label (attrs, task)); |  | ||||||
| 	} | 	} | ||||||
|  | 	return str_steal (&stats); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void | static void | ||||||
| @ -2185,6 +2130,7 @@ app_layout_mpd_status (void) | |||||||
| 	chtype attrs[2] = { APP_ATTR (NORMAL), APP_ATTR (HIGHLIGHT) }; | 	chtype attrs[2] = { APP_ATTR (NORMAL), APP_ATTR (HIGHLIGHT) }; | ||||||
| 	app_push (&l, g.ui->padding (attrs[0], 0.25, 1)); | 	app_push (&l, g.ui->padding (attrs[0], 0.25, 1)); | ||||||
| 
 | 
 | ||||||
|  | 	struct str_map *map = &g.playback_info; | ||||||
| 	if (g.active_tab->item_mark > -1) | 	if (g.active_tab->item_mark > -1) | ||||||
| 	{ | 	{ | ||||||
| 		struct tab_range r = tab_selection_range (g.active_tab); | 		struct tab_range r = tab_selection_range (g.active_tab); | ||||||
| @ -2193,14 +2139,18 @@ app_layout_mpd_status (void) | |||||||
| 		app_push_fill (&l, g.ui->label (attrs[0], msg)); | 		app_push_fill (&l, g.ui->label (attrs[0], msg)); | ||||||
| 		free (msg); | 		free (msg); | ||||||
| 	} | 	} | ||||||
|  | 	else if (g.poller_curl.registered) | ||||||
|  | 		app_push_fill (&l, g.ui->label (attrs[0], "Downloading...")); | ||||||
|  | 	else if (str_map_find (map, "updating_db")) | ||||||
|  | 		app_push_fill (&l, g.ui->label (attrs[0], "Updating database...")); | ||||||
| 	else | 	else | ||||||
| 	{ | 	{ | ||||||
| 		app_layout_mpd_status_playlist (&l, attrs[0]); | 		char *status = app_mpd_status_playlist (); | ||||||
| 		l.tail->width = -1; | 		app_push_fill (&l, g.ui->label (attrs[0], status)); | ||||||
|  | 		free (status); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	const char *s = NULL; | 	const char *s; | ||||||
| 	struct str_map *map = &g.playback_info; |  | ||||||
| 	bool repeat  = (s = str_map_find (map, "repeat"))  && strcmp (s, "0"); | 	bool repeat  = (s = str_map_find (map, "repeat"))  && strcmp (s, "0"); | ||||||
| 	bool random  = (s = str_map_find (map, "random"))  && strcmp (s, "0"); | 	bool random  = (s = str_map_find (map, "random"))  && strcmp (s, "0"); | ||||||
| 	bool single  = (s = str_map_find (map, "single"))  && strcmp (s, "0"); | 	bool single  = (s = str_map_find (map, "single"))  && strcmp (s, "0"); | ||||||
| @ -2769,11 +2719,8 @@ app_editor_process_action (enum action action) | |||||||
| 
 | 
 | ||||||
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | ||||||
| 
 | 
 | ||||||
| // Carefully chosen to limit the possibility of ever hitting termo keymods.
 |  | ||||||
| enum { APP_KEYMOD_DOUBLE_CLICK = 1 << 15 }; |  | ||||||
| 
 |  | ||||||
| static bool | static bool | ||||||
| app_process_left_mouse_click (struct widget *w, int x, int y, int modifiers) | app_process_left_mouse_click (struct widget *w, int x, int y, bool double_click) | ||||||
| { | { | ||||||
| 	switch (w->id) | 	switch (w->id) | ||||||
| 	{ | 	{ | ||||||
| @ -2811,19 +2758,12 @@ app_process_left_mouse_click (struct widget *w, int x, int y, int modifiers) | |||||||
| 		 || row_index >= (int) tab->item_count - tab->item_top) | 		 || row_index >= (int) tab->item_count - tab->item_top) | ||||||
| 			return false; | 			return false; | ||||||
| 
 | 
 | ||||||
| 		if (!(modifiers & TERMO_KEYMOD_SHIFT)) |  | ||||||
| 			tab->item_mark = -1; |  | ||||||
| 		else if (!tab->can_multiselect || tab->item_selected < 0) |  | ||||||
| 			return false; |  | ||||||
| 		else if (tab->item_mark < 0) |  | ||||||
| 			tab->item_mark = tab->item_selected; |  | ||||||
| 
 |  | ||||||
| 		// TODO: Probably will need to fix up item->top
 | 		// TODO: Probably will need to fix up item->top
 | ||||||
| 		//   for partially visible items in X11.
 | 		//   for partially visible items in X11.
 | ||||||
| 		tab->item_selected = row_index + tab->item_top; | 		tab->item_selected = row_index + tab->item_top; | ||||||
| 		app_invalidate (); | 		app_invalidate (); | ||||||
| 
 | 
 | ||||||
| 		if (modifiers & APP_KEYMOD_DOUBLE_CLICK) | 		if (double_click) | ||||||
| 			app_process_action (ACTION_CHOOSE); | 			app_process_action (ACTION_CHOOSE); | ||||||
| 		break; | 		break; | ||||||
| 	} | 	} | ||||||
| @ -2844,7 +2784,7 @@ app_process_left_mouse_click (struct widget *w, int x, int y, int modifiers) | |||||||
| 
 | 
 | ||||||
| static bool | static bool | ||||||
| app_process_mouse (termo_mouse_event_t type, int x, int y, int button, | app_process_mouse (termo_mouse_event_t type, int x, int y, int button, | ||||||
| 	int modifiers) | 	bool double_click) | ||||||
| { | { | ||||||
| 	// XXX: Terminals don't let us know which button has been released,
 | 	// XXX: Terminals don't let us know which button has been released,
 | ||||||
| 	//   so we can't press buttons at that point.  We'd need a special "click"
 | 	//   so we can't press buttons at that point.  We'd need a special "click"
 | ||||||
| @ -2868,7 +2808,7 @@ app_process_mouse (termo_mouse_event_t type, int x, int y, int button, | |||||||
| 
 | 
 | ||||||
| 		x -= target->x; | 		x -= target->x; | ||||||
| 		y -= target->y; | 		y -= target->y; | ||||||
| 		return app_process_left_mouse_click (target, x, y, modifiers); | 		return app_process_left_mouse_click (target, x, y, double_click); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (g.editor.line) | 	if (g.editor.line) | ||||||
| @ -2891,7 +2831,7 @@ app_process_mouse (termo_mouse_event_t type, int x, int y, int button, | |||||||
| 	{ | 	{ | ||||||
| 	case 1: | 	case 1: | ||||||
| 		g.ui_dragging = target->id; | 		g.ui_dragging = target->id; | ||||||
| 		return app_process_left_mouse_click (target, x, y, modifiers); | 		return app_process_left_mouse_click (target, x, y, double_click); | ||||||
| 	case 4: | 	case 4: | ||||||
| 		if (target->id == WIDGET_LIST) | 		if (target->id == WIDGET_LIST) | ||||||
| 			return app_process_action (ACTION_SCROLL_UP); | 			return app_process_action (ACTION_SCROLL_UP); | ||||||
| @ -4389,8 +4329,6 @@ spectrum_setup_fifo (void) | |||||||
| 		get_config_string (g.config.root, "settings.spectrum_format"); | 		get_config_string (g.config.root, "settings.spectrum_format"); | ||||||
| 	struct config_item *spectrum_bars = | 	struct config_item *spectrum_bars = | ||||||
| 		config_item_get (g.config.root, "settings.spectrum_bars", NULL); | 		config_item_get (g.config.root, "settings.spectrum_bars", NULL); | ||||||
| 	struct config_item *spectrum_fps = |  | ||||||
| 		config_item_get (g.config.root, "settings.spectrum_fps", NULL); |  | ||||||
| 	if (!spectrum_path) | 	if (!spectrum_path) | ||||||
| 		return; | 		return; | ||||||
| 
 | 
 | ||||||
| @ -4402,8 +4340,8 @@ spectrum_setup_fifo (void) | |||||||
| 		print_error ("spectrum: %s", "FIFO path could not be resolved"); | 		print_error ("spectrum: %s", "FIFO path could not be resolved"); | ||||||
| 	else if (!g.locale_is_utf8) | 	else if (!g.locale_is_utf8) | ||||||
| 		print_error ("spectrum: %s", "UTF-8 locale required"); | 		print_error ("spectrum: %s", "UTF-8 locale required"); | ||||||
| 	else if (!spectrum_init (&g.spectrum, (char *) spectrum_format, | 	else if (!spectrum_init (&g.spectrum, | ||||||
| 		spectrum_bars->value.integer, spectrum_fps->value.integer, &e)) | 		(char *) spectrum_format, spectrum_bars->value.integer, &e)) | ||||||
| 	{ | 	{ | ||||||
| 		print_error ("spectrum: %s", e->message); | 		print_error ("spectrum: %s", e->message); | ||||||
| 		error_free (e); | 		error_free (e); | ||||||
| @ -5160,10 +5098,28 @@ tui_make_scrollbar (chtype attrs) | |||||||
| static void | static void | ||||||
| tui_render_list (struct widget *self) | tui_render_list (struct widget *self) | ||||||
| { | { | ||||||
| 	LIST_FOR_EACH (struct widget, w, self->on_sublayout (self)) | 	struct tab *tab = g.active_tab; | ||||||
|  | 	int to_show = | ||||||
|  | 		MIN (app_visible_items (), (int) tab->item_count - tab->item_top); | ||||||
|  | 	for (int row = 0; row < to_show; row++) | ||||||
| 	{ | 	{ | ||||||
| 		w->on_render (w); | 		int item_index = tab->item_top + row; | ||||||
| 		free (w); | 		struct widget *head = app_layout_row (tab, item_index); | ||||||
|  | 		widget_redistribute (head, self->width); | ||||||
|  | 
 | ||||||
|  | 		int x = self->x; | ||||||
|  | 		int y = self->y + row * g.ui_vunit; | ||||||
|  | 		LIST_FOR_EACH (struct widget, w, head) | ||||||
|  | 		{ | ||||||
|  | 			w->x += x; | ||||||
|  | 			w->y += y; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		LIST_FOR_EACH (struct widget, w, head) | ||||||
|  | 		{ | ||||||
|  | 			w->on_render (w); | ||||||
|  | 			free (w); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -5174,7 +5130,6 @@ tui_make_list (void) | |||||||
| 	w->width = -1; | 	w->width = -1; | ||||||
| 	w->height = g.active_tab->item_count; | 	w->height = g.active_tab->item_count; | ||||||
| 	w->on_render = tui_render_list; | 	w->on_render = tui_render_list; | ||||||
| 	w->on_sublayout = app_sublayout_list; |  | ||||||
| 	return w; | 	return w; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -5279,25 +5234,23 @@ tui_on_tty_event (termo_key_t *event, int64_t event_ts) | |||||||
| 	static int64_t last_event_ts; | 	static int64_t last_event_ts; | ||||||
| 	static int last_button; | 	static int last_button; | ||||||
| 
 | 
 | ||||||
| 	int y, x, button, y_last, x_last, modifiers = 0; | 	int y, x, button, y_last, x_last; | ||||||
| 	termo_mouse_event_t type, type_last; | 	termo_mouse_event_t type, type_last; | ||||||
| 	if (termo_interpret_mouse (g.tk, event, &type, &button, &y, &x)) | 	if (termo_interpret_mouse (g.tk, event, &type, &button, &y, &x)) | ||||||
| 	{ | 	{ | ||||||
| 		if (termo_interpret_mouse | 		bool double_click = termo_interpret_mouse | ||||||
| 			(g.tk, &last_event, &type_last, NULL, &y_last, &x_last) | 			(g.tk, &last_event, &type_last, NULL, &y_last, &x_last) | ||||||
| 		 && event_ts - last_event_ts < 500 | 			&& event_ts - last_event_ts < 500 | ||||||
| 		 && type_last == TERMO_MOUSE_RELEASE && type == TERMO_MOUSE_PRESS | 			&& type_last == TERMO_MOUSE_RELEASE && type == TERMO_MOUSE_PRESS | ||||||
| 		 && y_last == y && x_last == x && last_button == button) | 			&& y_last == y && x_last == x && last_button == button; | ||||||
| 		{ | 		if (!app_process_mouse (type, x, y, button, double_click)) | ||||||
| 			modifiers |= APP_KEYMOD_DOUBLE_CLICK; | 			beep (); | ||||||
| 			// Prevent interpreting triple clicks as two double clicks.
 | 
 | ||||||
|  | 		// Prevent interpreting triple clicks as two double clicks
 | ||||||
|  | 		if (double_click) | ||||||
| 			last_button = 0; | 			last_button = 0; | ||||||
| 		} |  | ||||||
| 		else if (type == TERMO_MOUSE_PRESS) | 		else if (type == TERMO_MOUSE_PRESS) | ||||||
| 			last_button = button; | 			last_button = button; | ||||||
| 
 |  | ||||||
| 		if (!app_process_mouse (type, x, y, button, modifiers)) |  | ||||||
| 			beep (); |  | ||||||
| 	} | 	} | ||||||
| 	else if (!app_process_termo_event (event)) | 	else if (!app_process_termo_event (event)) | ||||||
| 		beep (); | 		beep (); | ||||||
| @ -5699,27 +5652,19 @@ x11_render_spectrum (struct widget *self) | |||||||
| 	x11_render_padding (self); | 	x11_render_padding (self); | ||||||
| 
 | 
 | ||||||
| #ifdef WITH_FFTW | #ifdef WITH_FFTW | ||||||
| 	XRectangle rectangles[g.spectrum.bars]; | 	int step = self->width / g.spectrum.bars; | ||||||
| 	int step = self->width / N_ELEMENTS (rectangles); |  | ||||||
| 	for (int i = 0; i < g.spectrum.bars; i++) | 	for (int i = 0; i < g.spectrum.bars; i++) | ||||||
| 	{ | 	{ | ||||||
| 		int height = round ((self->height - 2) * g.spectrum.spectrum[i]); | 		float value = g.spectrum.spectrum[i]; | ||||||
| 		rectangles[i] = (XRectangle) | 		int height = round ((self->height - 2) * value); | ||||||
| 		{ | 		XRenderFillRectangle (g.dpy, PictOpSrc, | ||||||
|  | 			g.x11_pixmap_picture, x11_fg (self), | ||||||
| 			self->x + i * step, | 			self->x + i * step, | ||||||
| 			self->y + self->height - 1 - height, | 			self->y + self->height - 1 - height, | ||||||
| 			step, | 			step, | ||||||
| 			height, | 			height); | ||||||
| 		}; |  | ||||||
| 	} | 	} | ||||||
| 
 | #endif   // WITH_FFTW
 | ||||||
| 	XRenderFillRectangles (g.dpy, PictOpSrc, g.x11_pixmap_picture, |  | ||||||
| 		x11_fg (self), rectangles, N_ELEMENTS (rectangles)); |  | ||||||
| #endif  // WITH_FFTW
 |  | ||||||
| 
 |  | ||||||
| 	// Enable the spectrum_redraw() hack.
 |  | ||||||
| 	XRectangle r = { self->x, self->y, self->width, self->height }; |  | ||||||
| 	XUnionRectWithRegion (&r, g.x11_clip, g.x11_clip); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static struct widget * | static struct widget * | ||||||
| @ -5768,10 +5713,28 @@ x11_render_list (struct widget *self) | |||||||
| { | { | ||||||
| 	x11_render_padding (self); | 	x11_render_padding (self); | ||||||
| 
 | 
 | ||||||
| 	LIST_FOR_EACH (struct widget, w, self->on_sublayout (self)) | 	struct tab *tab = g.active_tab; | ||||||
|  | 	int to_show = | ||||||
|  | 		MIN (app_visible_items (), (int) tab->item_count - tab->item_top); | ||||||
|  | 	for (int row = 0; row < to_show; row++) | ||||||
| 	{ | 	{ | ||||||
| 		w->on_render (w); | 		int item_index = tab->item_top + row; | ||||||
| 		free (w); | 		struct widget *head = app_layout_row (tab, item_index); | ||||||
|  | 		widget_redistribute (head, self->width); | ||||||
|  | 
 | ||||||
|  | 		int x = self->x; | ||||||
|  | 		int y = self->y + row * g.ui_vunit; | ||||||
|  | 		LIST_FOR_EACH (struct widget, w, head) | ||||||
|  | 		{ | ||||||
|  | 			w->x += x; | ||||||
|  | 			w->y += y; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		LIST_FOR_EACH (struct widget, w, head) | ||||||
|  | 		{ | ||||||
|  | 			w->on_render (w); | ||||||
|  | 			free (w); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -5780,7 +5743,6 @@ x11_make_list (void) | |||||||
| { | { | ||||||
| 	struct widget *w = xcalloc (1, sizeof *w + 1); | 	struct widget *w = xcalloc (1, sizeof *w + 1); | ||||||
| 	w->on_render = x11_render_list; | 	w->on_render = x11_render_list; | ||||||
| 	w->on_sublayout = app_sublayout_list; |  | ||||||
| 	return w; | 	return w; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -5829,26 +5791,20 @@ x11_render (void) | |||||||
| { | { | ||||||
| 	XRenderFillRectangle (g.dpy, PictOpSrc, g.x11_pixmap_picture, | 	XRenderFillRectangle (g.dpy, PictOpSrc, g.x11_pixmap_picture, | ||||||
| 		&x11_default_bg, 0, 0, g.ui_width, g.ui_height); | 		&x11_default_bg, 0, 0, g.ui_width, g.ui_height); | ||||||
|  | 
 | ||||||
|  | 	// TODO: Consider setting clip rectangles (not particularly needed).
 | ||||||
| 	LIST_FOR_EACH (struct widget, w, g.widgets.head) | 	LIST_FOR_EACH (struct widget, w, g.widgets.head) | ||||||
| 		if (w->width && w->height) | 		if (w->width && w->height) | ||||||
| 			w->on_render (w); | 			w->on_render (w); | ||||||
| 
 |  | ||||||
| 	XRectangle r = { 0, 0, g.ui_width, g.ui_height }; |  | ||||||
| 	XUnionRectWithRegion (&r, g.x11_clip, g.x11_clip); |  | ||||||
| 	poller_idle_set (&g.xpending_event); | 	poller_idle_set (&g.xpending_event); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void | static void | ||||||
| x11_flip (void) | x11_flip (void) | ||||||
| { | { | ||||||
| 	// This exercise in futility doesn't seem to affect CPU usage much.
 |  | ||||||
| 	XRectangle r = {}; |  | ||||||
| 	XClipBox (g.x11_clip, &r); |  | ||||||
| 	XCopyArea (g.dpy, g.x11_pixmap, g.x11_window, | 	XCopyArea (g.dpy, g.x11_pixmap, g.x11_window, | ||||||
| 		DefaultGC (g.dpy, DefaultScreen (g.dpy)), | 		DefaultGC (g.dpy, DefaultScreen (g.dpy)), | ||||||
| 		r.x, r.y, r.width, r.height, r.x, r.y); | 		0, 0, g.ui_width, g.ui_height, 0, 0); | ||||||
| 
 |  | ||||||
| 	XSubtractRegion (g.x11_clip, g.x11_clip, g.x11_clip); |  | ||||||
| 	poller_idle_set (&g.xpending_event); | 	poller_idle_set (&g.xpending_event); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -5857,14 +5813,12 @@ x11_destroy (void) | |||||||
| { | { | ||||||
| 	XDestroyIC (g.x11_ic); | 	XDestroyIC (g.x11_ic); | ||||||
| 	XCloseIM (g.x11_im); | 	XCloseIM (g.x11_im); | ||||||
| 	XDestroyRegion (g.x11_clip); |  | ||||||
| 	XDestroyWindow (g.dpy, g.x11_window); | 	XDestroyWindow (g.dpy, g.x11_window); | ||||||
| 	XRenderFreePicture (g.dpy, g.x11_pixmap_picture); | 	XRenderFreePicture (g.dpy, g.x11_pixmap_picture); | ||||||
| 	XFreePixmap (g.dpy, g.x11_pixmap); | 	XFreePixmap (g.dpy, g.x11_pixmap); | ||||||
| 	XftDrawDestroy (g.xft_draw); | 	XftDrawDestroy (g.xft_draw); | ||||||
| 	XftFontClose (g.dpy, g.xft_regular); | 	XftFontClose (g.dpy, g.xft_regular); | ||||||
| 	XftFontClose (g.dpy, g.xft_bold); | 	XftFontClose (g.dpy, g.xft_bold); | ||||||
| 	cstr_set (&g.x11_selection, NULL); |  | ||||||
| 
 | 
 | ||||||
| 	poller_fd_reset (&g.x11_event); | 	poller_fd_reset (&g.x11_event); | ||||||
| 	XCloseDisplay (g.dpy); | 	XCloseDisplay (g.dpy); | ||||||
| @ -6038,161 +5992,46 @@ x11_init_pixmap (void) | |||||||
| 		= XRenderCreatePicture (g.dpy, g.x11_pixmap, format, 0, NULL); | 		= XRenderCreatePicture (g.dpy, g.x11_pixmap, format, 0, NULL); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static char * |  | ||||||
| x11_find_text (struct widget *list, int x, int y) |  | ||||||
| { |  | ||||||
| 	struct widget *target = NULL; |  | ||||||
| 	LIST_FOR_EACH (struct widget, w, list) |  | ||||||
| 		if (x >= w->x && x < w->x + w->width |  | ||||||
| 		 && y >= w->y && y < w->y + w->height) |  | ||||||
| 			target = w; |  | ||||||
| 	if (!target) |  | ||||||
| 		return NULL; |  | ||||||
| 
 |  | ||||||
| 	if (target->on_sublayout) |  | ||||||
| 	{ |  | ||||||
| 		struct widget *sublist = target->on_sublayout (target); |  | ||||||
| 		char *result = x11_find_text (sublist, x, y); |  | ||||||
| 		LIST_FOR_EACH (struct widget, w, sublist) |  | ||||||
| 			free (w); |  | ||||||
| 		if (result) |  | ||||||
| 			return result; |  | ||||||
| 	} |  | ||||||
| 	return xstrdup (target->text); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // TODO: OSC 52 exists for terminals, so make it possible to enable that there.
 |  | ||||||
| static bool |  | ||||||
| x11_process_press (int x, int y, int button, int modifiers) |  | ||||||
| { |  | ||||||
| 	if (button != Button3) |  | ||||||
| 		goto out; |  | ||||||
| 
 |  | ||||||
| 	char *text = x11_find_text (g.widgets.head, x, y); |  | ||||||
| 	if (!text || !*(cstr_strip_in_place (text, " \t"))) |  | ||||||
| 	{ |  | ||||||
| 		free (text); |  | ||||||
| 		goto out; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	cstr_set (&g.x11_selection, text); |  | ||||||
| 	XSetSelectionOwner (g.dpy, XInternAtom (g.dpy, "CLIPBOARD", False), |  | ||||||
| 		g.x11_window, CurrentTime); |  | ||||||
| 
 |  | ||||||
| 	cstr_set (&g.message, |  | ||||||
| 		xstrdup_printf ("Text copied to clipboard: %s", g.x11_selection)); |  | ||||||
| 	poller_timer_set (&g.message_timer, 5000); |  | ||||||
| 	app_invalidate (); |  | ||||||
| 	return true; |  | ||||||
| 
 |  | ||||||
| out: |  | ||||||
| 	return app_process_mouse (TERMO_MOUSE_PRESS, x, y, button, modifiers); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static int |  | ||||||
| x11_state_to_modifiers (unsigned int state) |  | ||||||
| { |  | ||||||
| 	int modifiers = 0; |  | ||||||
| 	if (state & ShiftMask)    modifiers |= TERMO_KEYMOD_SHIFT; |  | ||||||
| 	if (state & ControlMask)  modifiers |= TERMO_KEYMOD_CTRL; |  | ||||||
| 	if (state & Mod1Mask)     modifiers |= TERMO_KEYMOD_ALT; |  | ||||||
| 	return modifiers; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool | static bool | ||||||
| on_x11_input_event (XEvent *ev) | on_x11_input_event (XEvent *ev) | ||||||
| { | { | ||||||
| 	static XEvent last_press_event; | 	static XEvent last_button_event; | ||||||
| 	if (ev->type == KeyPress) | 	if (ev->type == KeyPress) | ||||||
| 	{ | 	{ | ||||||
| 		last_press_event = (XEvent) {}; | 		last_button_event = (XEvent) {}; | ||||||
| 		return on_x11_keypress (ev); | 		return on_x11_keypress (ev); | ||||||
| 	} | 	} | ||||||
| 	if (ev->type == MotionNotify) | 	if (ev->type == MotionNotify) | ||||||
| 	{ | 	{ | ||||||
| 		return app_process_mouse (TERMO_MOUSE_DRAG, | 		// We only select for Button1MotionMask, so this works out.
 | ||||||
| 			ev->xmotion.x, ev->xmotion.y, 1 /* Button1MotionMask */, | 		int x = ev->xmotion.x, y = ev->xmotion.y; | ||||||
| 			x11_state_to_modifiers (ev->xmotion.state)); | 		return app_process_mouse (TERMO_MOUSE_DRAG, x, y, 1, false); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// This is nearly the same as tui_on_tty_event().
 | 	// See tui_on_tty_event().  Just here we know the button on button release.
 | ||||||
| 	int x = ev->xbutton.x, y = ev->xbutton.y; | 	int x = ev->xbutton.x, y = ev->xbutton.y; | ||||||
| 	unsigned int button = ev->xbutton.button; | 	unsigned int button = ev->xbutton.button; | ||||||
| 	int modifiers = x11_state_to_modifiers (ev->xbutton.state); | 	bool double_click = ev->xbutton.time - last_button_event.xbutton.time < 500 | ||||||
| 	if (ev->type == ButtonPress | 		&& last_button_event.type == ButtonRelease && ev->type == ButtonPress | ||||||
| 	 && ev->xbutton.time - last_press_event.xbutton.time < 500 | 		&& abs (last_button_event.xbutton.x - x) < 5 | ||||||
| 	 && abs (last_press_event.xbutton.x - x) < 5 | 		&& abs (last_button_event.xbutton.y - y) < 5 | ||||||
| 	 && abs (last_press_event.xbutton.y - y) < 5 | 		&& last_button_event.xbutton.button == button; | ||||||
| 	 && last_press_event.xbutton.button == button) | 
 | ||||||
| 	{ | 	// Prevent interpreting triple clicks as two double clicks.
 | ||||||
| 		modifiers |= APP_KEYMOD_DOUBLE_CLICK; | 	// FIXME: This doesn't work: we skip ButtonPress, but use ButtonRelease.
 | ||||||
| 		// Prevent interpreting triple clicks as two double clicks.
 | 	last_button_event = (XEvent) {}; | ||||||
| 		last_press_event = (XEvent) {}; | 	if (!double_click) | ||||||
| 	} | 		last_button_event = *ev; | ||||||
| 	else if (ev->type == ButtonPress) |  | ||||||
| 		last_press_event = *ev; |  | ||||||
| 
 | 
 | ||||||
| 	if (ev->type == ButtonPress) | 	if (ev->type == ButtonPress) | ||||||
| 		return x11_process_press (x, y, button, modifiers); | 		return app_process_mouse | ||||||
|  | 			(TERMO_MOUSE_PRESS, x, y, button, double_click); | ||||||
| 	if (ev->type == ButtonRelease) | 	if (ev->type == ButtonRelease) | ||||||
| 		return app_process_mouse | 		return app_process_mouse | ||||||
| 			(TERMO_MOUSE_RELEASE, x, y, button, modifiers); | 			(TERMO_MOUSE_RELEASE, x, y, button, double_click); | ||||||
| 	return false; | 	return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void |  | ||||||
| on_x11_selection_request (XSelectionRequestEvent *ev) |  | ||||||
| { |  | ||||||
| 	Atom xa_targets = XInternAtom (g.dpy, "TARGETS", False); |  | ||||||
| 	Atom xa_compound_text = XInternAtom (g.dpy, "COMPOUND_TEXT", False); |  | ||||||
| 	Atom xa_utf8 = XInternAtom (g.dpy, "UTF8_STRING", False); |  | ||||||
| 	Atom targets[] = { xa_targets, XA_STRING, xa_compound_text, xa_utf8 }; |  | ||||||
| 
 |  | ||||||
| 	bool ok = false; |  | ||||||
| 	Atom property = ev->property ? ev->property : ev->target; |  | ||||||
| 	if (!g.x11_selection) |  | ||||||
| 		goto out; |  | ||||||
| 
 |  | ||||||
| 	XICCEncodingStyle style = 0; |  | ||||||
| 	if ((ok = ev->target == xa_targets)) |  | ||||||
| 	{ |  | ||||||
| 		XChangeProperty (g.dpy, ev->requestor, property, |  | ||||||
| 			XA_ATOM, 32, PropModeReplace, |  | ||||||
| 			(const unsigned char *) targets, N_ELEMENTS (targets)); |  | ||||||
| 		goto out; |  | ||||||
| 	} |  | ||||||
| 	else if (ev->target == XA_STRING) |  | ||||||
| 		style = XStringStyle; |  | ||||||
| 	else if (ev->target == xa_compound_text) |  | ||||||
| 		style = XCompoundTextStyle; |  | ||||||
| 	else if (ev->target == xa_utf8) |  | ||||||
| 		style = XUTF8StringStyle; |  | ||||||
| 	else |  | ||||||
| 		goto out; |  | ||||||
| 
 |  | ||||||
| 	// XXX: We let it crash us with BadLength, but we may, e.g., use INCR.
 |  | ||||||
| 	XTextProperty text = {}; |  | ||||||
| 	if ((ok = !Xutf8TextListToTextProperty |  | ||||||
| 		 (g.dpy, &g.x11_selection, 1, style, &text))) |  | ||||||
| 	{ |  | ||||||
| 		XChangeProperty (g.dpy, ev->requestor, property, |  | ||||||
| 			text.encoding, text.format, PropModeReplace, |  | ||||||
| 			text.value, text.nitems); |  | ||||||
| 	} |  | ||||||
| 	XFree (text.value); |  | ||||||
| 
 |  | ||||||
| out: |  | ||||||
| 	XEvent response = {}; |  | ||||||
| 	response.xselection.type = SelectionNotify; |  | ||||||
| 	// XXX: We should check it against the event causing XSetSelectionOwner().
 |  | ||||||
| 	response.xselection.time = ev->time; |  | ||||||
| 	response.xselection.requestor = ev->requestor; |  | ||||||
| 	response.xselection.selection = ev->selection; |  | ||||||
| 	response.xselection.target = ev->target; |  | ||||||
| 	response.xselection.property = ok ? property : None; |  | ||||||
| 	XSendEvent (g.dpy, ev->requestor, False, 0, &response); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void | static void | ||||||
| on_x11_event (XEvent *ev) | on_x11_event (XEvent *ev) | ||||||
| { | { | ||||||
| @ -6200,13 +6039,9 @@ on_x11_event (XEvent *ev) | |||||||
| 	switch (ev->type) | 	switch (ev->type) | ||||||
| 	{ | 	{ | ||||||
| 	case Expose: | 	case Expose: | ||||||
| 	{ | 		if (!ev->xexpose.count) | ||||||
| 		XRectangle r = { ev->xexpose.x, ev->xexpose.y, | 			poller_idle_set (&g.flip_event); | ||||||
| 			ev->xexpose.width, ev->xexpose.height }; |  | ||||||
| 		XUnionRectWithRegion (&r, g.x11_clip, g.x11_clip); |  | ||||||
| 		poller_idle_set (&g.flip_event); |  | ||||||
| 		break; | 		break; | ||||||
| 	} |  | ||||||
| 	case ConfigureNotify: | 	case ConfigureNotify: | ||||||
| 		if (g.ui_width == ev->xconfigure.width | 		if (g.ui_width == ev->xconfigure.width | ||||||
| 		 && g.ui_height == ev->xconfigure.height) | 		 && g.ui_height == ev->xconfigure.height) | ||||||
| @ -6221,12 +6056,6 @@ on_x11_event (XEvent *ev) | |||||||
| 		XftDrawChange (g.xft_draw, g.x11_pixmap); | 		XftDrawChange (g.xft_draw, g.x11_pixmap); | ||||||
| 		app_invalidate (); | 		app_invalidate (); | ||||||
| 		break; | 		break; | ||||||
| 	case SelectionRequest: |  | ||||||
| 		on_x11_selection_request (&ev->xselectionrequest); |  | ||||||
| 		break; |  | ||||||
| 	case SelectionClear: |  | ||||||
| 		cstr_set (&g.x11_selection, NULL); |  | ||||||
| 		break; |  | ||||||
| 	case UnmapNotify: | 	case UnmapNotify: | ||||||
| 		app_quit (); | 		app_quit (); | ||||||
| 		break; | 		break; | ||||||
| @ -6285,11 +6114,6 @@ on_x11_error (Display *dpy, XErrorEvent *event) | |||||||
| 	 || (event->error_code == BadDrawable && event->resourceid == g.x11_window)) | 	 || (event->error_code == BadDrawable && event->resourceid == g.x11_window)) | ||||||
| 		return app_quit (), 0; | 		return app_quit (), 0; | ||||||
| 
 | 
 | ||||||
| 	// XXX: The simplest possible way of discarding selection management errors.
 |  | ||||||
| 	//   XCB would be a small win here, but it is a curse at the same time.
 |  | ||||||
| 	if (event->error_code == BadWindow && event->resourceid != g.x11_window) |  | ||||||
| 		return 0; |  | ||||||
| 
 |  | ||||||
| 	return x11_default_error_handler (dpy, event); | 	return x11_default_error_handler (dpy, event); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -6451,7 +6275,6 @@ x11_init (void) | |||||||
| 	g.x11_window = XCreateWindow (g.dpy, RootWindow (g.dpy, screen), 100, 100, | 	g.x11_window = XCreateWindow (g.dpy, RootWindow (g.dpy, screen), 100, 100, | ||||||
| 		g.ui_width, g.ui_height, 0, CopyFromParent, InputOutput, visual, | 		g.ui_width, g.ui_height, 0, CopyFromParent, InputOutput, visual, | ||||||
| 		CWEventMask | CWBackPixel | CWBitGravity, &attrs); | 		CWEventMask | CWBackPixel | CWBitGravity, &attrs); | ||||||
| 	g.x11_clip = XCreateRegion (); |  | ||||||
| 
 | 
 | ||||||
| 	XTextProperty prop = {}; | 	XTextProperty prop = {}; | ||||||
| 	char *name = PROGRAM_NAME; | 	char *name = PROGRAM_NAME; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user