win, event: delayed handling of reparent notify
Instead of handling reparent notify on the spot by updating the client windows, setup a flag on the window and call win_recheck_client later. This makes handling of complex scenarios easier. As example, see the case in issue #299. Note this is not a complete fix for #299
This commit is contained in:
		
							
								
								
									
										59
									
								
								src/event.c
									
									
									
									
									
								
							
							
						
						
									
										59
									
								
								src/event.c
									
									
									
									
									
								
							| @ -336,28 +336,35 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t | ||||
| 		    ps->c, ev->window, XCB_CW_EVENT_MASK, | ||||
| 		    (const uint32_t[]){determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN)}); | ||||
|  | ||||
| 		// Check if the window is an undetected client window | ||||
| 		// Firstly, check if it's a known client window | ||||
| 		if (!w_top) { | ||||
| 			// If not, look for its frame window | ||||
| 		if (!wid_has_prop(ps, ev->window, ps->atoms->aWM_STATE)) { | ||||
| 			log_debug("Window %#010x doesn't have WM_STATE property, it is " | ||||
| 			          "probably not a client window. But we will listen for " | ||||
| 			          "property change in case it gains one.", | ||||
| 			          ev->window); | ||||
| 			xcb_change_window_attributes( | ||||
| 			    ps->c, ev->window, XCB_CW_EVENT_MASK, | ||||
| 			    (const uint32_t[]){determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN) | | ||||
| 			                       XCB_EVENT_MASK_PROPERTY_CHANGE}); | ||||
| 		} else { | ||||
| 			auto w_real_top = find_managed_window_or_parent(ps, ev->parent); | ||||
| 			// If found, and the client window has not been determined, or its | ||||
| 			// frame may not have a correct client, continue | ||||
| 			if (w_real_top && (!w_real_top->client_win || | ||||
| 			                   w_real_top->client_win == w_real_top->base.id)) { | ||||
| 				// If it has WM_STATE, mark it the client window | ||||
| 				if (wid_has_prop(ps, ev->window, ps->atoms->aWM_STATE)) { | ||||
| 					w_real_top->wmwin = false; | ||||
| 					win_unmark_client(ps, w_real_top); | ||||
| 					win_mark_client(ps, w_real_top, ev->window); | ||||
| 				} | ||||
| 				// Otherwise, watch for WM_STATE on it | ||||
| 			if (w_real_top && w_real_top->state != WSTATE_UNMAPPED && | ||||
| 			    w_real_top->state != WSTATE_UNMAPPING) { | ||||
| 				log_debug("Mark window %#010x (%s) as having a stale " | ||||
| 				          "client", | ||||
| 				          w_real_top->base.id, w_real_top->name); | ||||
| 				win_set_flags(w_real_top, WIN_FLAGS_CLIENT_STALE); | ||||
| 				ps->pending_updates = true; | ||||
| 			} else { | ||||
| 				if (!w_real_top) | ||||
| 					log_debug("parent %#010x not found", ev->parent); | ||||
| 				else { | ||||
| 					xcb_change_window_attributes( | ||||
| 					    ps->c, ev->window, XCB_CW_EVENT_MASK, | ||||
| 					    (const uint32_t[]){ | ||||
| 					        determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN) | | ||||
| 					        XCB_EVENT_MASK_PROPERTY_CHANGE}); | ||||
| 					// Window is not currently mapped, unmark its | ||||
| 					// client to trigger a client recheck when it is | ||||
| 					// mapped later. | ||||
| 					win_unmark_client(ps, w_real_top); | ||||
| 					log_debug("parent %#010x (%s) is in state %d", | ||||
| 					          w_real_top->base.id, w_real_top->name, | ||||
| 					          w_real_top->state); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| @ -451,13 +458,11 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t | ||||
| 			                                 ps, ev->window, WIN_EVMODE_UNKNOWN)}); | ||||
|  | ||||
| 			auto w_top = find_managed_window_or_parent(ps, ev->window); | ||||
| 			// Initialize client_win as early as possible | ||||
| 			if (w_top && | ||||
| 			    (!w_top->client_win || w_top->client_win == w_top->base.id) && | ||||
| 			    wid_has_prop(ps, ev->window, ps->atoms->aWM_STATE)) { | ||||
| 				w_top->wmwin = false; | ||||
| 				win_unmark_client(ps, w_top); | ||||
| 				win_mark_client(ps, w_top, ev->window); | ||||
| 			// ev->window might have not been managed yet, in that case w_top | ||||
| 			// would be NULL. | ||||
| 			if (w_top) { | ||||
| 				win_set_flags(w_top, WIN_FLAGS_CLIENT_STALE); | ||||
| 				ps->pending_updates = true; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
							
								
								
									
										15
									
								
								src/win.c
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								src/win.c
									
									
									
									
									
								
							| @ -53,6 +53,8 @@ static const int WIN_GET_LEADER_MAX_RECURSION = 20; | ||||
| static const int ROUNDED_PIXELS = 1; | ||||
| static const double ROUNDED_PERCENT = 0.05; | ||||
|  | ||||
| static void win_recheck_client(session_t *ps, struct managed_win *w); | ||||
|  | ||||
| /// Generate a "return by value" function, from a function that returns the | ||||
| /// region via a region_t pointer argument. | ||||
| /// Function signature has to be (win *, region_t *) | ||||
| @ -323,8 +325,8 @@ void win_release_images(struct backend_base *backend, struct managed_win *w) { | ||||
| void win_process_flags(session_t *ps, struct managed_win *w) { | ||||
| 	if (win_check_flags_all(w, WIN_FLAGS_MAPPED)) { | ||||
| 		map_win_start(ps, w); | ||||
| 		win_clear_flags(w, WIN_FLAGS_MAPPED); | ||||
| 	} | ||||
| 	win_clear_flags(w, WIN_FLAGS_MAPPED); | ||||
|  | ||||
| 	// Not a loop | ||||
| 	while (win_check_flags_any(w, WIN_FLAGS_IMAGES_STALE) && | ||||
| @ -367,7 +369,14 @@ void win_process_flags(session_t *ps, struct managed_win *w) { | ||||
| 	} | ||||
|  | ||||
| 	// Clear stale image flags | ||||
| 	win_clear_flags(w, WIN_FLAGS_IMAGES_STALE); | ||||
| 	if (win_check_flags_any(w, WIN_FLAGS_IMAGES_STALE)) { | ||||
| 		win_clear_flags(w, WIN_FLAGS_IMAGES_STALE); | ||||
| 	} | ||||
|  | ||||
| 	if (win_check_flags_all(w, WIN_FLAGS_CLIENT_STALE)) { | ||||
| 		win_recheck_client(ps, w); | ||||
| 		win_clear_flags(w, WIN_FLAGS_CLIENT_STALE); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /** | ||||
| @ -2326,6 +2335,7 @@ win_is_fullscreen_xcb(xcb_connection_t *c, const struct atom *a, const xcb_windo | ||||
|  | ||||
| /// Set flags on a window. Some sanity checks are performed | ||||
| void win_set_flags(struct managed_win *w, uint64_t flags) { | ||||
| 	log_debug("Set flags %lu to window %#010x (%s)", flags, w->base.id, w->name); | ||||
| 	if (unlikely(w->state == WSTATE_DESTROYING)) { | ||||
| 		log_error("Flags set on a destroyed window %#010x (%s)", w->base.id, w->name); | ||||
| 		return; | ||||
| @ -2336,6 +2346,7 @@ void win_set_flags(struct managed_win *w, uint64_t flags) { | ||||
|  | ||||
| /// Clear flags on a window. Some sanity checks are performed | ||||
| void win_clear_flags(struct managed_win *w, uint64_t flags) { | ||||
| 	log_debug("Clear flags %lu from window %#010x (%s)", flags, w->base.id, w->name); | ||||
| 	if (unlikely(w->state == WSTATE_DESTROYING)) { | ||||
| 		log_error("Flags cleared on a destroyed window %#010x (%s)", w->base.id, | ||||
| 		          w->name); | ||||
|  | ||||
| @ -81,6 +81,8 @@ enum win_flags { | ||||
| 	WIN_FLAGS_SHADOW_STALE = 8, | ||||
| 	/// shadow has not been generated | ||||
| 	WIN_FLAGS_SHADOW_NONE = 16, | ||||
| 	/// the client window needs to be updated | ||||
| 	WIN_FLAGS_CLIENT_STALE = 32, | ||||
| 	/// the window is mapped by X, we need to call map_win_start for it | ||||
| 	WIN_FLAGS_MAPPED = 64, | ||||
| }; | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Yuxuan Shui
					Yuxuan Shui