Home | History | Annotate | Line # | Download | only in dist
      1 /*
      2  * Copyright 1992 Claude Lecommandeur.
      3  */
      4 
      5 #include "ctwm.h"
      6 
      7 #include <assert.h>
      8 #include <stdio.h>
      9 #include <stdlib.h>
     10 
     11 #include <X11/Xatom.h>
     12 
     13 #include "ctwm_atoms.h"
     14 #include "cursor.h"
     15 #include "icons.h"
     16 #include "list.h"
     17 #include "otp.h"
     18 #include "screen.h"
     19 #include "vscreen.h"
     20 #include "win_utils.h"
     21 
     22 
     23 static void DisplayWinUnchecked(VirtualScreen *vs, TwmWindow *tmp_win);
     24 
     25 
     26 static void init_def_vscreen(ScreenInfo *scr)
     27 {
     28 	VirtualScreen *vs = malloc(sizeof(VirtualScreen));
     29 
     30 	vs->x      = 0;
     31 	vs->y      = 0;
     32 	vs->w      = scr->rootw;
     33 	vs->h      = scr->rooth;
     34 	vs->window = scr->Root;
     35 	vs->next   = NULL;
     36 	vs->wsw    = 0;
     37 	scr->vScreenList = vs;
     38 	scr->currentvs   = vs;
     39 #ifdef VSCREEN
     40 	scr->numVscreens = 1;
     41 #endif
     42 	return;
     43 }
     44 
     45 
     46 void InitVirtualScreens(ScreenInfo *scr)
     47 {
     48 #ifndef VSCREEN
     49 	// Just do the faking if vscreens are all off anyway.
     50 	init_def_vscreen(scr);
     51 	return;
     52 #else
     53 
     54 	// Real implementation
     55 	Cursor cursor;
     56 	unsigned long valuemask, attrmask;
     57 	XSetWindowAttributes attributes;
     58 	name_list *nptr;
     59 	VirtualScreen *vs00 = NULL;
     60 
     61 	NewFontCursor(&cursor, "X_cursor");
     62 
     63 	if(scr->VirtualScreens == NULL) {
     64 		init_def_vscreen(scr);
     65 		return;
     66 	}
     67 	scr->numVscreens = 0;
     68 
     69 	attrmask  = ColormapChangeMask | EnterWindowMask | PropertyChangeMask |
     70 	            SubstructureRedirectMask | KeyPressMask | ButtonPressMask |
     71 	            ButtonReleaseMask;
     72 
     73 	valuemask = CWBackPixel | CWOverrideRedirect | CWEventMask | CWCursor;
     74 	attributes.override_redirect = True;
     75 	attributes.event_mask        = attrmask;
     76 	attributes.cursor            = cursor;
     77 	attributes.background_pixel  = Scr->Black;
     78 
     79 	scr->vScreenList = NULL;
     80 	for(nptr = Scr->VirtualScreens; nptr != NULL; nptr = nptr->next) {
     81 		VirtualScreen *vs;
     82 		char *geometry = (char *) nptr->name;
     83 		int x = 0, y = 0;
     84 		unsigned int w = 0, h = 0;
     85 
     86 		XParseGeometry(geometry, &x, &y, &w, &h);
     87 
     88 		if((x < 0) || (y < 0) || (w > scr->rootw) || (h > scr->rooth)) {
     89 			fprintf(stderr, "InitVirtualScreens : invalid geometry : %s\n", geometry);
     90 			continue;
     91 		}
     92 		vs = malloc(sizeof(VirtualScreen));
     93 		vs->x = x;
     94 		vs->y = y;
     95 		vs->w = w;
     96 		vs->h = h;
     97 		vs->window = XCreateWindow(dpy, Scr->Root, x, y, w, h,
     98 		                           0, CopyFromParent, CopyFromParent,
     99 		                           CopyFromParent, valuemask, &attributes);
    100 		vs->wsw = 0;
    101 
    102 		XSync(dpy, 0);
    103 		XMapWindow(dpy, vs->window);
    104 		XChangeProperty(dpy, vs->window, XA_WM_VIRTUALROOT, XA_STRING, 8,
    105 		                PropModeReplace, (unsigned char *) "Yes", 4);
    106 
    107 		vs->next = scr->vScreenList;
    108 		scr->vScreenList = vs;
    109 		Scr->numVscreens++;
    110 
    111 		/*
    112 		 * Remember which virtual screen is at (0,0).
    113 		 */
    114 		if(x == 0 && y == 0) {
    115 			vs00 = vs;
    116 		}
    117 	}
    118 
    119 	if(scr->vScreenList == NULL) {
    120 		fprintf(stderr, "no valid VirtualScreens found, exiting...\n");
    121 		exit(1);
    122 	}
    123 	/* Setup scr->{currentvs,Root{,x,y,w,h}} as if the
    124 	 * _correct_ virtual screen is entered with the mouse.
    125 	 * See HandleEnterNotify().
    126 	 */
    127 	if(vs00 == NULL) {
    128 		vs00 = scr->vScreenList;
    129 	}
    130 
    131 	Scr->Root  = vs00->window;
    132 #ifdef CAPTIVE
    133 	Scr->rootx = Scr->crootx + vs00->x;
    134 	Scr->rooty = Scr->crooty + vs00->y;
    135 #else
    136 	Scr->rootx = vs00->x;
    137 	Scr->rooty = vs00->y;
    138 #endif
    139 	Scr->rootw = vs00->w;
    140 	Scr->rooth = vs00->h;
    141 	Scr->currentvs = vs00;
    142 #endif  // VSCREEN
    143 }
    144 
    145 #ifdef VSCREEN
    146 VirtualScreen *findIfVScreenOf(int x, int y)
    147 {
    148 	VirtualScreen *vs;
    149 	for(vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
    150 
    151 		if((x >= vs->x) && ((x - vs->x) < vs->w) &&
    152 		                (y >= vs->y) && ((y - vs->y) < vs->h)) {
    153 			return vs;
    154 		}
    155 	}
    156 	return NULL;
    157 }
    158 #endif
    159 
    160 /*
    161  * Returns the order that virtual screens are displayed for the vscreen
    162  * list.  This is stored this way so everything ends up in the right place
    163  * on a ctwm restart.
    164  */
    165 char *
    166 CtwmGetVScreenMap(Display *display, Window rootw)
    167 {
    168 	unsigned char       *prop;
    169 	unsigned long       bytesafter;
    170 	unsigned long       len;
    171 	Atom                actual_type;
    172 	int                 actual_format;
    173 	char                *ret;
    174 
    175 	if(XA_WM_CTWM_VSCREENMAP == None) {
    176 		return false;
    177 	}
    178 	if(XGetWindowProperty(display, rootw, XA_WM_CTWM_VSCREENMAP, 0L, 512,
    179 	                      False, XA_STRING, &actual_type, &actual_format, &len,
    180 	                      &bytesafter, &prop) != Success) {
    181 		return NULL;
    182 	}
    183 	if(len == 0) {
    184 		return NULL;
    185 	}
    186 
    187 	ret = malloc(len + 1);
    188 	memcpy(ret, prop, len);
    189 	ret[len] = '\0';
    190 	XFree(prop);
    191 
    192 	return ret;
    193 }
    194 
    195 bool
    196 CtwmSetVScreenMap(Display *display, Window rootw,
    197                   struct VirtualScreen *firstvs)
    198 {
    199 	char                        buf[1024];
    200 	int                         tally = 0;
    201 	struct VirtualScreen        *vs;
    202 
    203 	if(XA_WM_CTWM_VSCREENMAP == None) {
    204 		return false;
    205 	}
    206 
    207 	memset(buf, 0, sizeof(buf));
    208 	for(vs = firstvs; vs; vs = vs->next) {
    209 		if(tally) {
    210 			strcat(buf, ",");
    211 		}
    212 		if(vs->wsw && vs->wsw->currentwspc && vs->wsw->currentwspc->name) {
    213 			strcat(buf, vs->wsw->currentwspc->name);
    214 			tally++;
    215 		}
    216 	}
    217 
    218 	if(! tally) {
    219 		return false;
    220 	}
    221 
    222 	XChangeProperty(display, rootw, XA_WM_CTWM_VSCREENMAP, XA_STRING, 8,
    223 	                PropModeReplace, (unsigned char *)buf, strlen(buf));
    224 	return true;
    225 }
    226 
    227 
    228 /*
    229  * Display a window in a given virtual screen.
    230  */
    231 void
    232 DisplayWin(VirtualScreen *vs, TwmWindow *tmp_win)
    233 {
    234 	OtpCheckConsistency();
    235 	DisplayWinUnchecked(vs, tmp_win);
    236 	OtpCheckConsistency();
    237 }
    238 
    239 static void
    240 DisplayWinUnchecked(VirtualScreen *vs, TwmWindow *tmp_win)
    241 {
    242 	/*
    243 	 * A window cannot be shown in multiple virtual screens, even if
    244 	 * it occupies both corresponding workspaces.
    245 	 */
    246 	if(vs && tmp_win->vs) {
    247 		return;
    248 	}
    249 
    250 	/* This is where we're moving it */
    251 	tmp_win->vs = vs;
    252 
    253 
    254 	/* If it's unmapped, RFAI() moves the necessary bits here */
    255 	if(!tmp_win->mapped) {
    256 		ReparentFrameAndIcon(tmp_win);
    257 
    258 		/* If it's got an icon that should be up, make it up here */
    259 		if(tmp_win->isicon) {
    260 			if(tmp_win->icon_on) {
    261 				if(tmp_win->icon && tmp_win->icon->w) {
    262 
    263 					IconUp(tmp_win);
    264 					XMapWindow(dpy, tmp_win->icon->w);
    265 				}
    266 			}
    267 		}
    268 
    269 		/* All there is to do with unmapped wins */
    270 		return;
    271 	}
    272 
    273 
    274 	/* If we make it this far, the window is mapped */
    275 
    276 	if(tmp_win->UnmapByMovingFarAway) {
    277 		/*
    278 		 * XXX I don't believe the handling of UnmapByMovingFarAway is
    279 		 * quite correct.
    280 		 */
    281 		if(vs) {
    282 			XReparentWindow(dpy, tmp_win->frame, vs->window,
    283 			                tmp_win->frame_x, tmp_win->frame_y);
    284 		}
    285 		else {
    286 			XMoveWindow(dpy, tmp_win->frame, tmp_win->frame_x, tmp_win->frame_y);
    287 		}
    288 	}
    289 	else {
    290 		/* Map and move it here */
    291 		if(!tmp_win->squeezed) {
    292 			long eventMask;
    293 
    294 			eventMask = mask_out_event(tmp_win->w, StructureNotifyMask);
    295 			XMapWindow(dpy, tmp_win->w);
    296 			restore_mask(tmp_win->w, eventMask);
    297 		}
    298 
    299 		ReparentFrameAndIcon(tmp_win);
    300 
    301 		XMapWindow(dpy, tmp_win->frame);
    302 		SetMapStateProp(tmp_win, NormalState);
    303 	}
    304 }
    305 
    306 
    307 /*
    308  * Move a window's frame and icon to a new VS.  This mostly happens as a
    309  * backend bit of the DisplayWin() process, but it does get called
    310  * directly for the Occupy window.  XXX Should it?
    311  */
    312 void
    313 ReparentFrameAndIcon(TwmWindow *tmp_win)
    314 {
    315 	VirtualScreen *vs = tmp_win->vs; /* which virtual screen we want it in */
    316 
    317 	/* parent_vs is the current real parent of the window */
    318 	if(vs != tmp_win->parent_vs) {
    319 		struct Icon *icon = tmp_win->icon;
    320 
    321 		// This must always be something...
    322 		assert(vs != NULL);
    323 
    324 		tmp_win->parent_vs = vs;
    325 
    326 		if(icon && icon->w) {
    327 			ReparentWindowAndIcon(dpy, tmp_win, vs->window,
    328 			                      tmp_win->frame_x, tmp_win->frame_y,
    329 			                      icon->w_x, icon->w_y);
    330 		}
    331 		else {
    332 			ReparentWindow(dpy, tmp_win,  WinWin, vs->window,
    333 			               tmp_win->frame_x, tmp_win->frame_y);
    334 		}
    335 	}
    336 }
    337 
    338 
    339 /*
    340  * Get this window outta here.  Note that despite naming, this is
    341  * unrelated to f.vanish.
    342  */
    343 void
    344 Vanish(VirtualScreen *vs, TwmWindow *tmp_win)
    345 {
    346 	/* It's not here?  Nothing to do. */
    347 	if(vs && tmp_win->vs && tmp_win->vs != vs) {
    348 		return;
    349 	}
    350 
    351 	/* Unmap (or near-equivalent) all its bits */
    352 	if(tmp_win->UnmapByMovingFarAway) {
    353 		/* UnmapByMovingFarAway?  Move it off-screen */
    354 		XMoveWindow(dpy, tmp_win->frame, Scr->rootw + 1, Scr->rooth + 1);
    355 	}
    356 	else if(tmp_win->mapped) {
    357 		/* It's mapped; unmap it */
    358 		long eventMask;
    359 
    360 		eventMask = mask_out_event(tmp_win->w, StructureNotifyMask);
    361 		XUnmapWindow(dpy, tmp_win->w);
    362 		XUnmapWindow(dpy, tmp_win->frame);
    363 		restore_mask(tmp_win->w, eventMask);
    364 
    365 		if(!tmp_win->DontSetInactive) {
    366 			SetMapStateProp(tmp_win, InactiveState);
    367 		}
    368 	}
    369 	else if(tmp_win->icon_on && tmp_win->icon && tmp_win->icon->w) {
    370 		/* It's not mapped, but the icon's up; hide it away */
    371 		XUnmapWindow(dpy, tmp_win->icon->w);
    372 		IconDown(tmp_win);
    373 	}
    374 
    375 #if 0
    376 	/*
    377 	 * The purpose of this is in the event of a ctwm death/restart,
    378 	 * geometries of windows that were on unmapped workspaces will show
    379 	 * up where they belong.
    380 	 * XXX - I doubt its usefulness, since still-mapped windows won't
    381 	 * enjoy this "protection", making it suboptimal at best.
    382 	 * XXX - XReparentWindow() messes up the stacking order of windows.
    383 	 * It should be avoided as much as possible. This already affects
    384 	 * switching away from and back to a workspace. Therefore do this only
    385 	 * if there are at least 2 virtual screens AND the new one (firstvs)
    386 	 * differs from where the window currently is. (Olaf Seibert).
    387 	 */
    388 
    389 	if(Scr->numVscreens > 1) {
    390 		int x, y;
    391 		unsigned int junk;
    392 		Window junkW, w = tmp_win->frame;
    393 		VirtualScreen *firstvs = NULL;
    394 
    395 		for(firstvs = Scr->vScreenList; firstvs; firstvs = firstvs->next)
    396 			if(firstvs->x == 0 && firstvs->y == 0) {
    397 				break;
    398 			}
    399 		if(firstvs && firstvs != vs) {
    400 			tmp_win->vs = firstvs;
    401 			ReparentFrameAndIcon(tmp_win);
    402 		}
    403 	}
    404 #endif
    405 
    406 	/* Currently displayed nowhere */
    407 	tmp_win->vs = NULL;
    408 }
    409