1/*The Panache Window Manager*/ 2/*By George Peter Staplin*/ 3/*Please read the LICENSE file included with the Panache distribution 4 *for usage restrictions. 5 */ 6 7#include <stdio.h> 8#include <stdlib.h> 9#include <signal.h> 10#ifndef __STDC__ 11 #include <malloc.h> 12#endif 13#include <X11/Xlib.h> 14#include <X11/Xutil.h> 15#include <X11/Xos.h> 16#include <X11/Xatom.h> 17#include <X11/keysym.h> 18#include <X11/cursorfont.h> 19#include <tcl.h> 20#include "PanacheWindowList.h" 21 22/*Style 23I use if (returnFromFunc == 1) instead of if (returnFromFunc) 24I use if (returnFromFunc == 0) instead of if (!returnFromFunc) 25*/ 26 27/*Automatic focus of new windows yes/no.*/ 28/*Automatic focus of transient windows yes/no.*/ 29 30#define PANACHE_DIRECTORY "Panache" 31#define CMD_ARGS (ClientData clientData, Tcl_Interp *interp, \ 32int objc, Tcl_Obj *CONST objv[]) 33 34Display *dis; 35XEvent report; 36Window root; 37Tcl_Interp *interp; 38int distance_from_edge = 0; 39Window mapped_window = None; 40int screen; 41Atom _XA_WM_STATE; 42Atom _XA_WM_PROTOCOLS; 43Atom _XA_WM_DELETE_WINDOW; 44Window workspace_manager; 45struct CList *keepAboveWindowList; 46unsigned long eventMask = (ResizeRedirectMask | PropertyChangeMask | \ 47 EnterWindowMask | LeaveWindowMask | FocusChangeMask | KeyPressMask); 48 49#define winIdLength 14 50/*#define FORK_ON_START*/ 51 52int PanacheGetWMState (Window win); 53void PanacheSelectInputForRootParented (Window win); 54void PanacheConfigureNormalWindow (Window win, unsigned long value_mask); 55 56char Panache_Init_script[] = { 57 "if {[file exists $prefix/$panacheDirectory/Panache.tcl] != 1} {\n" 58 " puts stderr {unable to open Panache.tcl Did you run make install?}\n" 59 " puts stderr \"I looked in $prefix/$panacheDirectory\"\n" 60 " exit -1\n" 61 "}\n" 62 "proc sendToPipe str {\n" 63 " set str [string map {\"\n\" \"\" \"\r\" \"\"} $str]\n" 64 " puts $::pipe $str\n" 65 " flush $::pipe\n" 66 "}\n" 67 "proc getFromPipe {} {\n" 68 " gets $::pipe line\n" 69 " if {$line != \"\"} {\n" 70 " set cmd [lindex $line 0]\n" 71 " if {[llength $line] == 2} {\n" 72 " $cmd [lindex $line 1]\n" 73 " } else {\n" 74 " eval $line\n" 75 " }\n" 76 " }\n" 77 "}\n" 78 "set ::pipe [open \"|$wishInterpreter $prefix/$panacheDirectory/Panache.tcl\" w+]\n" 79 "fconfigure $::pipe -blocking 0\n" 80 "\n"}; 81 82 83char *charMalloc (int size) { 84 char *mem = NULL; 85 86 mem = (char *) malloc ((sizeof (char)) * size); 87 88 if (mem == NULL) { 89 fprintf (stderr, "malloc failed to allocate memory This means that Panache \ 90and other applications could have problems if they continue running.\n\n \ 91exiting Panache now!"); 92 exit (-1); 93 } 94 95 return mem; 96} 97 98 99void sendConfigureNotify (Window win, unsigned long value_mask, XWindowChanges *winChanges) { 100 XEvent xe; 101 XWindowAttributes wattr; 102 103 if (XGetWindowAttributes (dis, win, &wattr) == 0) { 104 return; 105 } 106 107 xe.type = ConfigureNotify; 108 xe.xconfigure.type = ConfigureNotify; 109 xe.xconfigure.event = win; 110 xe.xconfigure.window = win; 111 112 113 xe.xconfigure.x = (value_mask & CWX) ? winChanges->x : wattr.x; 114 xe.xconfigure.y = (value_mask & CWY) ? winChanges->y : wattr.y; 115 xe.xconfigure.width = (value_mask & CWWidth) ? winChanges->width : wattr.width; 116 xe.xconfigure.height = (value_mask & CWHeight) ? winChanges->height : wattr.height; 117 118 xe.xconfigure.border_width = 0; 119 xe.xconfigure.above = None; 120 xe.xconfigure.override_redirect = 0; 121 122 XSendEvent (dis, win, 0, StructureNotifyMask, &xe); 123 124 XFlush (dis); 125} 126 127 128void sendMapNotify (Window win) { 129 XEvent mapNotify; 130 131 mapNotify.type = MapNotify; 132 mapNotify.xmap.type = MapNotify; 133 mapNotify.xmap.window = win; 134 mapNotify.xmap.display = dis; 135 mapNotify.xmap.event = win; 136 XSendEvent (dis, win, 0, StructureNotifyMask, &mapNotify); 137 XFlush (dis); 138} 139 140 141int PanacheAddAllWindowsCmd CMD_ARGS { 142 Window dummy; 143 Window *children = NULL; 144 unsigned int nchildren; 145 unsigned int i; 146 char *winId; 147 char *transientForWinId; 148 char str[] = "sendToPipe [list add [list $winTitle] $winId $winType $transientForWinId]"; 149 Window twin; 150 151 XSync (dis, 0); 152 /*XGrabServer (dis);*/ 153 154 if (XQueryTree (dis, 155 root, 156 &dummy, 157 &dummy, 158 &children, 159 &nchildren) == 0) { 160 161 fprintf (stderr, "Error querying the tree for the root window.\n"); 162 } 163 164 for (i = 0; i < nchildren; i++) { 165 XTextProperty xtp; 166 XWMHints *wmHints = XGetWMHints (dis, children[i]); 167 XWindowAttributes wattr; 168 169 xtp.value = NULL; 170 171 if (wmHints == NULL) { 172 continue; 173 } 174 175 if (wmHints->flags & IconWindowHint) { 176 continue; 177 } 178 179 if (XGetWindowAttributes (dis, children[i], &wattr) == 0) { 180 continue; 181 } 182 183 if (wattr.override_redirect == 1) { 184 continue; 185 } 186 187 if (wmHints->flags & StateHint) { 188 if (wmHints->initial_state & WithdrawnState) { 189 continue; 190 } else if (wattr.map_state == 0 && PanacheGetWMState (children[i]) == 0) { 191 continue; 192 } 193 } 194 195 XFree (wmHints); 196 197 XGetWMName (dis, children[i], &xtp); 198 199 winId = charMalloc (winIdLength); 200 201 sprintf (winId, "%ld", children[i]); 202 Tcl_SetVar (interp, "winTitle", (char *) xtp.value, 0); 203 Tcl_SetVar (interp, "winId", winId, 0); 204 205 if (XGetTransientForHint (dis, children[i], &twin) == 1) { 206 Tcl_SetVar (interp, "winType", "transient", 0); 207 208 transientForWinId = charMalloc (winIdLength); 209 sprintf (transientForWinId, "%ld", twin); 210 Tcl_SetVar (interp, "transientForWinId", transientForWinId, 0); 211 free (transientForWinId); 212 213 PanacheSelectInputForRootParented (children[i]); 214 215 } else { 216 Tcl_SetVar (interp, "winType", "normal", 0); 217 Tcl_SetVar (interp, "transientForWinId", "", 0); 218 219 /*Maybe I should compare the first char and then do strcmp?*/ 220 if (xtp.value != NULL && strcmp ((char *)xtp.value, "___Panache_GUI") != 0) { 221 PanacheConfigureNormalWindow (children[i], CWX|CWY|CWWidth|CWHeight); 222 PanacheSelectInputForRootParented (children[i]); 223 } 224 } 225 226 XFree (xtp.value); 227 free (winId); 228 229 if (Tcl_Eval (interp, str) != TCL_OK) { 230 fprintf (stderr, "Error in PanacheAddAllWindowsCmd: %s\n", Tcl_GetStringResult (interp)); 231 } 232 } 233 234 if (children != NULL) { 235 XFree (children); 236 } 237 238 /*XUngrabServer (dis);*/ 239 XSync (dis, 0); 240 241 return TCL_OK; 242} 243 244 245void PanacheConfigureRequest (XConfigureRequestEvent *event) { 246 XWindowChanges wc; 247 Window twin; 248 int maxWidth; 249 int maxHeight; 250 251 if (event->parent != root) { 252 return; 253 } 254 255#ifdef DEBUG 256 fprintf (stderr, "ConfigureRequest win %ld\n", event->window); 257 fprintf (stderr, "CWSibling %d\n", (event->value_mask & CWSibling) == 1); 258 fprintf (stderr, "CWStackMode %d\n", (event->value_mask & CWStackMode) == 1); 259#endif 260 261 maxWidth = (DisplayWidth (dis, screen) - distance_from_edge - 4); 262 maxHeight = DisplayHeight (dis, screen); 263 264 wc.border_width = 0; 265 wc.sibling = None; 266 wc.stack_mode = Above; 267 268 if (event->window == workspace_manager) { 269 wc.width = distance_from_edge; 270 wc.height = maxHeight; 271 wc.x = 0; 272 wc.y = 0; 273 XConfigureWindow(dis, event->window, CWX|CWY|CWWidth|CWHeight, &wc); 274 sendConfigureNotify (event->window, CWX|CWY|CWWidth|CWHeight, &wc); 275 return; 276 } else { 277 PanacheSelectInputForRootParented (event->window); 278 } 279 280 if (XGetTransientForHint (dis, event->window, &twin) == 1) { 281 if (event->width > maxWidth) { 282 wc.width = maxWidth; 283 } else { 284 wc.width = event->width; 285 } 286 287 wc.height = event->height; 288 289 if (event->x < distance_from_edge) { 290 wc.x = distance_from_edge; 291 } else { 292 wc.x = event->x; 293 } 294 295 wc.y = event->y; 296 XConfigureWindow (dis, event->window, event->value_mask, &wc); 297 sendConfigureNotify (event->window, event->value_mask, &wc); 298 } else { 299 PanacheConfigureNormalWindow (event->window, event->value_mask); 300 } 301 302 XFlush (dis); 303} 304 305 306/*This configures the window and sends a ConfigureNotify event. 307 *It's designed for normal non-transient windows 308 */ 309void PanacheConfigureNormalWindow ( 310 Window win, unsigned long value_mask) 311{ 312 XWindowChanges wc; 313 XSizeHints sizeHints; 314 long ljunk = 0; 315 int maxWidth = (DisplayWidth (dis, screen) - distance_from_edge - 4); 316 int maxHeight = DisplayHeight (dis, screen); 317 318 wc.border_width = 0; 319 wc.sibling = None; 320 wc.stack_mode = Above; 321 322 wc.x = distance_from_edge; 323 wc.y = 0; 324 wc.width = maxWidth; 325 wc.height = maxHeight; 326 327 if (XGetWMNormalHints (dis, win, &sizeHints, &ljunk)) { 328 if (sizeHints.flags & PMaxSize) { 329 wc.width = (sizeHints.max_width > maxWidth) ? maxWidth : sizeHints.max_width; 330 wc.height = (sizeHints.max_height > maxHeight) ? maxHeight : sizeHints.max_height; 331#ifdef DEBUG 332 fprintf (stderr, "MaxSize %d %d\n", sizeHints.max_width, sizeHints.max_height); 333#endif 334 } 335#ifdef DEBUG 336 if (sizeHints.flags & PResizeInc) { 337 fprintf (stderr, "PResizeInc\n"); 338 fprintf (stderr, "incr %d %d\n", sizeHints.width_inc, sizeHints.height_inc); 339 } 340 if (sizeHints.flags & PAspect) { 341 fprintf (stderr, "PAspect x %d\n", sizeHints.min_aspect.x); 342 } 343#endif 344 } 345 346 XConfigureWindow (dis, win, value_mask, &wc); 347 sendConfigureNotify (win, value_mask, &wc); 348} 349 350 351/*This appends windows that are not to be managed by 352 *Panache to a list, and Panache will later on raise 353 *them above other windows. 354 */ 355void PanacheCreateNotify (XCreateWindowEvent *event) { 356 357 if (event->override_redirect == 0 || event->parent != root) { 358 return; 359 } 360 361 CListAppend (keepAboveWindowList, event->window); 362} 363 364/*X has told Panache that a DestroyNotify event occured 365 *to a child of the root window, so Panache removes the 366 *window from the window list. 367 */ 368void PanacheDestroyNotify (XDestroyWindowEvent *event) { 369 Window win; 370 char *winId; 371 char str[] = "sendToPipe [list remove $winId]"; 372 373 win = event->window; 374 375 winId = charMalloc (winIdLength); 376 sprintf (winId, "%ld", win); 377 378 Tcl_SetVar (interp, "winId", winId, 0); 379 free (winId); 380 381#ifdef DEBUG 382 fprintf (stderr, "DestroyNotify\n"); 383#endif 384 385 CListRemove (keepAboveWindowList, event->window); 386 387 /*Tell Panache_GUI to remove the window*/ 388 if (Tcl_Eval (interp, str) != TCL_OK) { 389 fprintf (stderr, "Tcl_Eval error in PanacheDestroyNotify %s\n", Tcl_GetStringResult (interp)); 390 } 391} 392 393 394/*Panache_GUI calls this to send WM_DELETE_WINDOW or 395 *invoke XKillClient (if the window doesn't support 396 *WM_DELETE_WINDOW). We can't use XKillClient on all 397 *windows, because if the application has multiple 398 *toplevel windows sending XKillClient would destroy 399 *them all. 400 */ 401int PanacheDestroyCmd CMD_ARGS { 402 XClientMessageEvent ev; 403 Window win; 404 Atom *wmProtocols = NULL; 405 Atom *protocol; 406 int i; 407 int numAtoms; 408 int handlesWM_DELETE_WINDOW = 0; 409 410 411 Tcl_GetLongFromObj (interp, objv[1], (long *) &win); 412 413 if (XGetWMProtocols (dis, win, &wmProtocols, &numAtoms) == 1) { 414 for (i = 0, protocol = wmProtocols; i < numAtoms; i++, protocol++) { 415 if (*protocol == (Atom)_XA_WM_DELETE_WINDOW) { 416 handlesWM_DELETE_WINDOW = 1; 417 } 418 } 419 if (wmProtocols) { 420 XFree (wmProtocols); 421 } 422 } 423 424 if (handlesWM_DELETE_WINDOW == 1) { 425 ev.type = ClientMessage; 426 ev.window = win; 427 ev.message_type = _XA_WM_PROTOCOLS; 428 ev.format = 32; 429 ev.data.l[0] = _XA_WM_DELETE_WINDOW; 430 ev.data.l[1] = CurrentTime; 431 XSendEvent (dis, win, 0, 0L, (XEvent *) &ev); 432 } else { 433 XKillClient (dis, win); 434 } 435 436 XFlush (dis); 437 438 return TCL_OK; 439} 440 441 442int PanacheDFECmd CMD_ARGS { 443 Tcl_GetIntFromObj (interp, objv[1], &distance_from_edge); 444 return TCL_OK; 445} 446 447 448/*Panache_GUI sends focus $winId to get here.*/ 449int PanacheFocusCmd CMD_ARGS { 450 Window win; 451 452 Tcl_GetLongFromObj (interp, objv[1], (long *) &win); 453 454 if (XSetInputFocus (dis, win, RevertToParent, CurrentTime) != 1) { 455 fprintf (stderr, "XSetInputFocus failure within PanacheFocusCmd()"); 456 } 457 458 XFlush (dis); 459 460 return TCL_OK; 461} 462 463 464int PanacheGetWMState (Window win) { 465 int returnValue = 0; 466 Atom type; 467 int ijunk; 468 unsigned long ljunk; 469 unsigned long *state = NULL; 470 471 XGetWindowProperty ( 472 dis, 473 win, 474 _XA_WM_STATE, 475 0L, 476 1L, 477 0, 478 _XA_WM_STATE, 479 &type, 480 &ijunk, 481 &ljunk, 482 &ljunk, 483 (unsigned char **) &state 484 ); 485 486 if (type == _XA_WM_STATE) { 487 returnValue = (int) *state; 488 } else { 489 /*Don't know what to do*/ 490 returnValue = 0; 491 } 492 493 if (state != NULL) { 494 XFree (state); 495 } 496 497 return returnValue; 498} 499 500/*A window to keep above has the override_redirect 501 *attribute set to 1. 502 */ 503 504void PanacheRaiseKeepAboveWindows () { 505 Window win; 506 507 CListRewind (keepAboveWindowList); 508 509 while ((win = CListGet (keepAboveWindowList)) != NULL) { 510 XRaiseWindow (dis, win); 511 } 512 513 XFlush (dis); 514} 515 516 517void PanacheRecursivelyGrabKey (Window win, int keycode) { 518 Window dummy; 519 Window *children = NULL; 520 unsigned int nchildren; 521 int i; 522 523 524 if (XQueryTree (dis, win, &dummy, &dummy, &children, &nchildren) == 0) { 525 return; 526 } 527 528 for (i = 0; i < nchildren; i++) { 529 PanacheRecursivelyGrabKey (children[i], keycode); 530 XGrabKey (dis, keycode, Mod1Mask, win, 1, GrabModeAsync, GrabModeSync); 531 } 532 533 if (children != NULL) { 534 XFree (children); 535 } 536} 537 538 539int PanacheReparentCmd CMD_ARGS { 540 Window newParent; 541 Window win; 542 543 Tcl_GetLongFromObj (interp, objv[1], (long *) &win); 544 Tcl_GetLongFromObj (interp, objv[2], (long *) &newParent); 545 546 XReparentWindow (dis, win, newParent, 0, 20); 547 548 return TCL_OK; 549} 550 551 552void PanacheSelectInputForRootParented (Window win) { 553 554 XSelectInput (dis, win, eventMask); 555} 556 557 558void PanacheSetWMState (Window win, int state) { 559 unsigned long data[2]; 560 data[0] = state; 561 data[1] = None; 562 563 XChangeProperty (dis, win, _XA_WM_STATE, _XA_WM_STATE, 32, 564 PropModeReplace, (unsigned char *) data, 2 565 ); 566 567 XSync (dis, 0); 568} 569 570 571int PanacheTransientCmd CMD_ARGS { 572 Window parent; 573 Window win; 574 575 Tcl_GetLongFromObj (interp, objv[1], (long *) &win); 576 Tcl_GetLongFromObj (interp, objv[2], (long *) &parent); 577 578 XSetTransientForHint (dis, win, parent); 579 580 return TCL_OK; 581} 582 583/*This sends a string to Panache_GUI with info about the window, 584 *such as its title and window id. This information is processed 585 *within Panache_GUI and if desired PanacheMapCmd will map the 586 *window. 587 */ 588void PanacheMapRequest (XMapRequestEvent *event) { 589 char *winId; 590 char *transientForWinId; 591 XTextProperty xtp; 592 char str[] = "sendToPipe [list add [list $winTitle] $winId $winType $transientForWinId]"; 593 Window twin; 594 595 if (event->window == NULL) { 596 return; 597 } 598 599 /*This makes the state iconic, so that if the user presses 600 *restart before mapping the window, the window will show up. 601 */ 602 PanacheSetWMState (event->window, IconicState); 603 604 xtp.value = NULL; 605 606 XGetWMName (dis, event->window, &xtp); 607 608 winId = charMalloc (winIdLength); 609 610 sprintf (winId, "%ld", event->window); 611 PanacheSelectInputForRootParented (event->window); 612 613 Tcl_SetVar (interp, "winTitle", (char *) xtp.value, 0); 614 Tcl_SetVar (interp, "winId", winId, 0); 615 616 if (XGetTransientForHint (dis, event->window, &twin) == 1) { 617 Tcl_SetVar (interp, "winType", "transient", 0); 618 transientForWinId = charMalloc (winIdLength); 619 sprintf (transientForWinId, "%ld", twin); 620 Tcl_SetVar (interp, "transientForWinId", transientForWinId, 0); 621 free (transientForWinId); 622 } else { 623 Tcl_SetVar (interp, "winType", "normal", 0); 624 Tcl_SetVar (interp, "transientForWinId", "", 0); 625 } 626 627 XFree (xtp.value); 628 free (winId); 629 630 if (Tcl_Eval (interp, str) != TCL_OK) { 631 fprintf (stderr, "Error in PanacheMapRequest: %s\n", Tcl_GetStringResult (interp)); 632 } 633} 634 635 636/*This maps a window. It may be called after PanacheMapRequest by 637 *Panache_GUI. This is also called when a window is over another 638 *window and the user selects the button for the window to display 639 *which causes this function to raise the window. 640 */ 641int PanacheMapCmd CMD_ARGS { 642 Window win; 643 Window twin; 644 XWindowAttributes winAttrib; 645 646 Tcl_GetLongFromObj (interp, objv[1], (long *) &win); 647 648 PanacheSelectInputForRootParented (win); 649 650 /*XGrabKey (dis, XK_Tab, Mod1Mask, win, 1, GrabModeAsync, GrabModeAsync);*/ 651 /*PanacheRecursivelyGrabKey (win, XK_Tab);*/ 652 653 XGetWindowAttributes (dis, win, &winAttrib); 654 655 if (winAttrib.x < distance_from_edge) { 656 winAttrib.x = distance_from_edge; 657 if (winAttrib.y < 0) { 658 winAttrib.y = 0; 659 } 660 XMoveWindow (dis, win, winAttrib.x, winAttrib.y); 661 } 662 663 if (XGetTransientForHint (dis, win, &twin) == 1) { 664 PanacheSetWMState (win, NormalState); 665 XMapRaised (dis, win); 666 sendMapNotify (win); 667 mapped_window = win; 668 PanacheRaiseKeepAboveWindows (); 669 670 return TCL_OK; 671 } 672 673 674 if ((PanacheGetWMState (win)) == 1) { 675 XRaiseWindow (dis, win); 676 PanacheRaiseKeepAboveWindows (); 677 678 return TCL_OK; 679 } 680 681 /*If we are here the window hasn't had its size set, or 682 *the WM_STATE was not 1. 683 */ 684 685 PanacheSetWMState (win, NormalState); 686 687 /*I've found that some applications get upset if you sent 688 *a ConfigureNotify before the MapNotify, when they are 689 *expecting the MapNotify to be eminent. 690 */ 691 692 XMapRaised (dis, win); 693 sendMapNotify (win); 694 695 PanacheConfigureNormalWindow (win, CWX|CWY|CWWidth|CWHeight); 696 697 mapped_window = win; 698 PanacheRaiseKeepAboveWindows (); 699 700 return TCL_OK; 701} 702 703 704int PanacheMapWorkspaceCmd CMD_ARGS { 705 XWindowChanges wc; 706 Window win; 707 708 Tcl_GetLongFromObj (interp, objv[1], (long *) &win); 709 workspace_manager = win; 710 PanacheSetWMState (win, NormalState); 711 712 wc.x = 0; 713 wc.y = 0; 714 wc.width = distance_from_edge; 715 wc.height = DisplayHeight (dis, screen); 716 717 XConfigureWindow(dis, win, CWX|CWY|CWWidth|CWHeight, &wc); 718 sendConfigureNotify (win, CWX|CWY|CWWidth|CWHeight, &wc); 719 720 XMapWindow (dis, win); 721 sendMapNotify (win); 722 mapped_window = win; 723 XFlush (dis); 724 725 return TCL_OK; 726} 727 728 729int PanacheMoveCmd CMD_ARGS { 730 XEvent event; 731 unsigned int buttonPressed; 732 Window wjunk; 733 int ijunk; 734 Cursor handCursor; 735 Window win; 736 int oldX; 737 int oldY; 738 int x; 739 int y; 740 int internalX; 741 int internalY; 742 unsigned int maskReturn; 743 int continueEventLoop = 1; 744 XWindowAttributes winAttr; 745 746 handCursor = XCreateFontCursor (dis, XC_hand2); 747 748 XGrabPointer (dis, root, 1, 749 ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | \ 750 PointerMotionHintMask, 751 GrabModeAsync, GrabModeAsync, 752 None, 753 handCursor, 754 CurrentTime 755 ); 756 757 /*Wait until the user has selected the window to move.*/ 758 XMaskEvent (dis, ButtonPressMask, &event); 759 760 /*The button being held down while dragging the window.*/ 761 buttonPressed = event.xbutton.button; 762 763 /*fprintf (stderr, "ButtonPressed %d\n", buttonPressed);*/ 764 765 XQueryPointer (dis, root, 766 &wjunk, &win, 767 &oldX, &oldY, 768 &internalX, &internalY, 769 &maskReturn 770 ); 771 772 if (win == workspace_manager) { 773 XUngrabPointer (dis, CurrentTime); 774 XFreeCursor (dis, handCursor); 775 XSync (dis, 0); 776 777 return TCL_OK; 778 } 779 780 781 XGetWindowAttributes (dis, win, &winAttr); 782 783 while (continueEventLoop == 1) { 784 XNextEvent (dis, &event); 785 switch (event.type) { 786 case ButtonRelease: 787 { 788 if (event.xbutton.button == buttonPressed) { 789 continueEventLoop = 0; 790 } 791 } 792 break; 793 case MotionNotify: 794 { 795 XWindowChanges wc; 796 int newX; 797 int newY; 798 799 while (XCheckTypedEvent (dis, MotionNotify, &event)); 800 801 XQueryPointer (dis, root, &wjunk, &wjunk, 802 &x, &y, 803 &ijunk, &ijunk, 804 &maskReturn 805 ); 806 807 newX = x - oldX + winAttr.x; 808 newY = y - oldY + winAttr.y; 809 810 if (newX < distance_from_edge) { 811 812 if (winAttr.override_redirect == 1) { 813 XMoveWindow (dis, win, distance_from_edge, newY); 814 } else { 815 wc.x = distance_from_edge; 816 wc.y = newY; 817 XConfigureWindow (dis, win, CWX | CWY, &wc); 818 sendConfigureNotify (win, CWX | CWY, &wc); 819 } 820 continue; 821 } 822 823 if (winAttr.override_redirect == 1) { 824 XMoveWindow (dis, win, newX, newY); 825 } else { 826 wc.x = newX; 827 wc.y = newY; 828 XConfigureWindow (dis, win, CWX | CWY, &wc); 829 sendConfigureNotify (win, CWX | CWY, &wc); 830 } 831 } 832 break; 833 } 834 } 835 836 /*fprintf (stderr, "win is %ld\n", win);*/ 837 838 XUngrabPointer (dis, CurrentTime); 839 XFreeCursor (dis, handCursor); 840 841 XSync (dis, 0); 842 843 return TCL_OK; 844} 845 846 847XErrorHandler PanacheErrorHandler (Display *dis, XErrorEvent *event) { 848/*I've discovered that errors are frequently timing problems. 849Maybe XSync would help in some areas. 850Most errors are not fatal. 851*/ 852 return 0; 853} 854 855 856int main() { 857 fd_set readfds; 858 int nfds; 859 int xFd; 860 int pipeFd; 861 int inputPipeFd; 862 ClientData data; 863 int fdsTcl; 864 865 866 dis = XOpenDisplay (NULL); 867 screen = DefaultScreen (dis); 868 root = RootWindow (dis, screen); 869 interp = Tcl_CreateInterp (); 870 871 XSetErrorHandler ((XErrorHandler) PanacheErrorHandler); 872 873 _XA_WM_STATE = XInternAtom (dis, "WM_STATE", 0); 874 _XA_WM_PROTOCOLS = XInternAtom (dis, "WM_PROTOCOLS", 0); 875 _XA_WM_DELETE_WINDOW = XInternAtom (dis, "WM_DELETE_WINDOW", 0); 876 877 keepAboveWindowList = CListInit (); 878 879#ifdef FORK_ON_START 880 { 881 int res; 882 res = fork(); 883 884 if (res == -1) { 885 fprintf (stderr, "Unable to fork process."); 886 return 1; 887 } 888 889 if (res != 0) { 890 exit (0); 891 } 892 } 893#endif 894 895 if (Tcl_Init (interp) != TCL_OK) { 896 printf ("Tcl_Init error\n"); 897 exit (-1); 898 } 899 900#define CREATE_CMD(cmdName,func) Tcl_CreateObjCommand (interp, \ 901cmdName, func, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL) 902 903 CREATE_CMD ("map_workspace", PanacheMapWorkspaceCmd); 904 CREATE_CMD ("distance_from_edge", PanacheDFECmd); 905 CREATE_CMD ("map", PanacheMapCmd); 906 CREATE_CMD ("destroy", PanacheDestroyCmd); 907 CREATE_CMD ("add_all_windows", PanacheAddAllWindowsCmd); 908 CREATE_CMD ("focus", PanacheFocusCmd); 909 CREATE_CMD ("transient", PanacheTransientCmd); 910 CREATE_CMD ("reparent", PanacheReparentCmd); 911 CREATE_CMD ("move", PanacheMoveCmd); 912 913 Tcl_SetVar (interp, "wishInterpreter", WISH_INTERPRETER, 0); 914 Tcl_SetVar (interp, "prefix", PREFIX, 0); 915 Tcl_SetVar (interp, "panacheDirectory", PANACHE_DIRECTORY, 0); 916 917 918 if (Tcl_Eval (interp, Panache_Init_script) != TCL_OK) { 919 fprintf (stderr, "Error while evaluating Panache_Init_script within main()%s\n", Tcl_GetStringResult (interp)); 920 exit (-1); 921 } 922 923 XSelectInput (dis, root, LeaveWindowMask | EnterWindowMask| \ 924 PropertyChangeMask | SubstructureRedirectMask | \ 925 SubstructureNotifyMask | KeyPressMask | KeyReleaseMask | \ 926 ResizeRedirectMask | FocusChangeMask 927 ); 928 929 xFd = ConnectionNumber (dis); 930 931 Tcl_GetChannelHandle (Tcl_GetChannel (interp, Tcl_GetVar (interp, "pipe", NULL), NULL), TCL_WRITABLE, &data); 932 pipeFd = (int) data; 933 /*fprintf (stderr, "pipeFd %d", pipeFd);*/ 934 935 Tcl_GetChannelHandle (Tcl_GetChannel (interp, Tcl_GetVar (interp, "pipe", NULL), NULL), TCL_READABLE, &data); 936 inputPipeFd = (int) data; 937 938 XFlush(dis); 939 940 for (;;) { 941 942 FD_ZERO (&readfds); 943 FD_SET (xFd, &readfds); 944 FD_SET (pipeFd, &readfds); 945 FD_SET (inputPipeFd, &readfds); 946 947 fdsTcl = (pipeFd > inputPipeFd) ? pipeFd : inputPipeFd; 948 nfds = (xFd > fdsTcl) ? xFd + 1: fdsTcl + 1; 949 950 select (nfds, &readfds, NULL, NULL, NULL); 951 952 if (FD_ISSET (inputPipeFd, &readfds) != 0) { 953 if (Tcl_Eval (interp, "getFromPipe") != TCL_OK) { 954 fprintf (stderr, "getFromPipe error %s\n", Tcl_GetStringResult (interp)); 955 } 956 } 957 958 if (FD_ISSET (pipeFd, &readfds) != 0) { 959 while (Tcl_DoOneEvent (TCL_DONT_WAIT)); 960 } 961 962 if (FD_ISSET (xFd, &readfds) == 0) { 963 continue; 964 } 965 966 while (XPending (dis) > 0) { 967 XNextEvent (dis, &report); 968 969 /*fprintf (stderr, "type %d\n", report.type);*/ 970 switch (report.type) { 971 case ConfigureNotify: 972 /*fprintf (stderr, "ConfigureNotify \n");*/ 973 break; 974 975 case CreateNotify: 976 PanacheCreateNotify (&report.xcreatewindow); 977 break; 978 979 case ConfigureRequest: 980 PanacheConfigureRequest (&report.xconfigurerequest); 981 break; 982 983 case DestroyNotify: 984 PanacheDestroyNotify (&report.xdestroywindow); 985 break; 986 987 case EnterNotify: 988 { 989 Window win = report.xcrossing.window; 990 char *winId = NULL; 991 char cmd[] = "sendToPipe [list activateWindow $winId]"; 992 993 winId = charMalloc (winIdLength); 994 sprintf (winId, "%ld", win); 995 Tcl_SetVar (interp, "winId", winId, 0); 996 free (winId); 997 998 if (Tcl_Eval (interp, cmd) != TCL_OK) { 999 fprintf (stderr, "Error evaluating cmd in EnterNotify within main() %s\n", Tcl_GetStringResult (interp)); 1000 } 1001 1002 } 1003 break; 1004 1005 case FocusIn: 1006 break; 1007 1008 1009 case KeyPress: 1010 { 1011 char cmd[] = "sendToPipe next"; 1012 1013 if (XLookupKeysym (&report.xkey, 0) == XK_Tab && (report.xkey.state & Mod1Mask)) { 1014 fprintf (stderr, "alt tab win %ld\n", report.xkey.window); 1015 if (Tcl_Eval (interp, cmd) != TCL_OK) { 1016 fprintf (stderr, "Error evaluating cmd in KeyPress within main() %s\n", Tcl_GetStringResult (interp)); 1017 } 1018 } else { 1019 /*Send XK_Tab*/ 1020 } 1021 1022 /* 1023 fprintf (stderr, "1 %d \n", report.xkey.state == Mod1Mask); 1024 fprintf (stderr, "2 %d \n", report.xkey.state == Mod2Mask); 1025 fprintf (stderr, "3 %d \n", report.xkey.state == Mod3Mask); 1026 fprintf (stderr, "4 %d \n", report.xkey.state == Mod4Mask); 1027 fprintf (stderr, "5 %d \n", report.xkey.state == Mod5Mask); 1028 */ 1029 } 1030 break; 1031 1032 case MapRequest: 1033 PanacheMapRequest (&report.xmaprequest); 1034 break; 1035 1036 case UnmapNotify: 1037 { 1038 int state = PanacheGetWMState (report.xunmap.window); 1039 /*Mapped or Iconified*/ 1040 if (state == 1 || state == 3) { 1041 char *winId = NULL; 1042 char cmd[] = "sendToPipe [list remove $winId]"; 1043 1044 winId = charMalloc (winIdLength); 1045 sprintf (winId, "%ld", report.xunmap.window); 1046 1047 Tcl_SetVar (interp, "winId", winId, 0); 1048 free (winId); 1049 1050 PanacheSetWMState (report.xunmap.window, WithdrawnState); 1051 1052 if (Tcl_Eval (interp, cmd) != TCL_OK) { 1053 fprintf (stderr, "Tcl_Eval error in UnmapNotify within main() %s", Tcl_GetStringResult (interp)); 1054 } 1055 } 1056 } 1057 break; 1058 1059 case PropertyNotify: 1060 { 1061 XTextProperty xtp; 1062 xtp.value = NULL; 1063 1064 if (XGetWMName (dis, report.xproperty.window, &xtp) == 1) { 1065 char *winId; 1066 char cmd[] = "sendToPipe [list title [list $winTitle] $winId]"; 1067 1068 winId = charMalloc (winIdLength); 1069 sprintf (winId, "%ld", report.xproperty.window); 1070 1071 Tcl_SetVar (interp, "winTitle", (char *) xtp.value, 0); 1072 Tcl_SetVar (interp, "winId", winId, 0); 1073 1074 free (winId); 1075 XFree (xtp.value); 1076 1077 if (Tcl_Eval (interp, cmd) != TCL_OK) { 1078 fprintf (stderr, "Tcl_Eval error in PropertyNotify: within main() %s\n", Tcl_GetStringResult (interp)); 1079 } 1080 } 1081 } 1082 break; 1083 1084 1085 case ReparentNotify: 1086 { 1087 Window win = report.xreparent.window; 1088 Window parent = report.xreparent.parent; 1089 1090 /* 1091 fprintf (stderr, "ReparentNotify\n"); 1092 fprintf (stderr, "win %ld parent %ld event %ld\n", win, parent, event); 1093 */ 1094 XSelectInput (dis, win, 0); 1095 1096 if (parent != root) { 1097 char *winId; 1098 char cmd[] = "sendToPipe [list remove $winId]"; 1099 1100 winId = charMalloc (winIdLength); 1101 sprintf (winId, "%ld", win); 1102 Tcl_SetVar (interp, "winId", winId, 0); 1103 free (winId); 1104 1105 if (Tcl_Eval (interp, cmd) != TCL_OK) { 1106 fprintf (stderr, "Tcl_Eval error in ReparentNotify within main() %s\n", Tcl_GetStringResult (interp)); 1107 } 1108 } 1109 } 1110 break; 1111 1112 1113 case ResizeRequest: 1114 { 1115 Window twin; 1116 Window win = report.xresizerequest.window; 1117 1118 if (XGetTransientForHint (dis, win, &twin) == 1) { 1119 XResizeWindow (dis, win, 1120 report.xresizerequest.width, report.xresizerequest.height 1121 ); 1122 } 1123 1124 XFlush (dis); 1125 } 1126 break; 1127 1128 default: 1129 break; 1130 } 1131 } 1132 } 1133 return 0; 1134} 1135