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 23static void DisplayWinUnchecked(VirtualScreen *vs, TwmWindow *tmp_win); 24 25 26static 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 46void 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 146VirtualScreen *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 */ 165char * 166CtwmGetVScreenMap(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 195bool 196CtwmSetVScreenMap(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 */ 231void 232DisplayWin(VirtualScreen *vs, TwmWindow *tmp_win) 233{ 234 OtpCheckConsistency(); 235 DisplayWinUnchecked(vs, tmp_win); 236 OtpCheckConsistency(); 237} 238 239static void 240DisplayWinUnchecked(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 */ 312void 313ReparentFrameAndIcon(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 */ 343void 344Vanish(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