#!/usr/bin/python # # mplwm.py -- Example PLWM window manager "configuration" # # Copyright (C) 1999,2000 Peter Liljenberg # Personalized 2000 Torbjörn Axelsson # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import sys import os ###SETUP PATH sys.path[1:1] = [os.path.join(sys.path[0], '..')] ###END SETUP PATH import time from plxlib import plxlib, X from plwm import wmanager, focus, keys, \ deltamove, \ border, color, font, cycle, views, \ modewinctl, modestatus from plwm.moveresize import MoveResizeKeys delta = deltamove.DeltaMove() class MyClient(wmanager.Client, focus.FocusClient, border.BorderClient, modestatus.ModeFocusedTitle): no_border_clients = ['XClock', 'XBiff', 'CDStatus', 'XModeWindow','xload'] start_iconified_clients = ['WMManager'] move_focus_ignore_clients = no_border_clients default_pointer_pos = {'Emacs': (-1, 0), 'XTerm': (-1, 0)} class MyScreen(wmanager.Screen, color.Color, modewinctl.ModeClientControl, modestatus.ModeStatus, modestatus.ModeMoveResize, views.XMW_ViewHandler, keys.KeyGrabber): view_always_visible_clients = ['XClock', 'XBiff', 'CDStatus', 'XModeWindow','xload'] class WMConfig: def __wm_init__(self): BasicKeys(self, self.dispatch) self.dispatch.add_handler('cmdevent', cmdhandler) class PLWM(wmanager.WindowManager, focus.SloppyFocus, font.Font, WMConfig): client_class = MyClient screen_class = MyScreen def cmdhandler(evt): print 'Exit:', evt.exitstatus(), 'Signal:', evt.termsig() class BasicKeys(keys.KeyHandler): def F1(self, event): self.wm.current_screen.view_find_with_client('XTerm') def S_F1(self, event): self.wm.system('xterm -geometry 80x50+200+100') def C_S_F1(self, event): self.wm.current_screen.view_new() self.wm.system('xterm -geometry 80x50+200+100') def F2(self, event): self.wm.current_screen.view_find_with_client('Emacs') def S_F2(self, event): self.wm.system('emacs') def C_S_F2(self, event): self.wm.current_screen.view_new() self.wm.system('emacs') def F3(self, event): self.wm.current_screen.view_find_with_client('Netscape') def S_F3(self, event): self.wm.system('netscape') def C_S_F3(self, event): self.wm.current_screen.view_new() self.wm.system('netscape') def F4(self, event): self.wm.current_screen.view_find_tag('Lysator') def S_F4(self, event): self.wm.current_screen.view_tag('Lysator') self.wm.system('ssh -f svenolov.lysator.liu.se /usr/openwin/bin/xterm -name lysator') def C_F4(self, event): self.wm.current_screen.view_tag('Lysator') def C_S_F4(self, event): self.wm.current_screen.view_new() self.wm.current_screen.view_tag('Lysator') self.wm.system('ssh -f svenolov.lysator.liu.se /usr/openwin/bin/xterm -name lysator') def F5(self, event): self.wm.current_screen.view_find_with_client('soffice') def S_F5(self, event): self.wm.system('/import/local/office52/program/soffice') def C_S_F5(self, event): self.wm.current_screen.view_new() self.wm.system('/import/local/office52/program/soffice') def F6(self, event): self.wm.current_screen.view_find_tag('F6') def S_F6(self, event): self.wm.current_screen.view_tag('F6') def Pause(self, event): clients = self.wm.query_clients() for c in clients: if c.client_in_list(['titrax']): c.deiconify() c.activate() return else: self.wm.system('/opt/local/bin/titrax') def KP_Begin(self, event): MyMoveResizeKeys(self, event) def C_Tab(self, event): wmanager.debug('keys', 'Into CycleUnmapped mode') try: mv = CycleUMKeys(self.wm, self.dispatch, event.time) except keys.error, status: wmanager.debug('keys', 'Grabbing keyboard failed: %d', status) def KP_Insert(self, event): wmanager.debug('keys', 'Iconifying') if self.wm.focus_client: self.wm.focus_client.iconify() def KP_Subtract(self, event): wmanager.debug('keys', 'Prev view') self.wm.current_screen.view_prev() def KP_Add(self, event): wmanager.debug('keys', 'Next view') self.wm.current_screen.view_next() def C_KP_Add(self, event): wmanager.debug('keys', 'New view') self.wm.current_screen.view_new() def KP_Left(self, event): self.wm.display.WarpPointer(-delta.get(event.time), 0) def KP_Right(self, event): self.wm.display.WarpPointer(delta.get(event.time), 0) def KP_Up(self, event): self.wm.display.WarpPointer(0, -delta.get(event.time)) def KP_Down(self, event): self.wm.display.WarpPointer(0, delta.get(event.time)) def KP_Home(self, event): d = delta.get(event.time) self.wm.display.WarpPointer(-d, -d) def KP_End(self, event): d = delta.get(event.time) self.wm.display.WarpPointer(-d, d) def KP_Prior(self, event): d = delta.get(event.time) self.wm.display.WarpPointer(d, -d) def KP_Next(self, event): d = delta.get(event.time) self.wm.display.WarpPointer(d, d) def KP_Enter(self, event): if self.wm.focus_client: self.wm.focus_client.configure({'stack_mode': X.Opposite}) # For laptop compitability KP_Divide = KP_Enter def C_KP_Subtract(self, event): self.wm.system('xlock -mode blank') def C_M_Escape(self, event): raise 'PLWMEscape', 'Escaping window manager' def C_KP_Delete(self, event): if self.wm.focus_client: self.wm.focus_client.delete(1) def C_S_KP_Delete(self, event): if self.wm.focus_client: self.wm.focus_client.destroy() def C_KP_Left(self, event): self.wm.move_focus(focus.MOVE_LEFT) def C_KP_Right(self, event): self.wm.move_focus(focus.MOVE_RIGHT) def C_KP_Up(self, event): self.wm.move_focus(focus.MOVE_UP) def C_KP_Down(self, event): self.wm.move_focus(focus.MOVE_DOWN) def C_less(self, event): self.wm.move_focus(focus.MOVE_LEFT) def C_S_less(self, event): self.wm.move_focus(focus.MOVE_RIGHT) # # Use the keymap for moving and resizing windows. # Without any modifiers moves, with Shift enlarges, with Ctrl shrinks # End with KP_5. Abort with Escape or KP_Delete. # class MyMoveResizeKeys(MoveResizeKeys): KP_Left = MoveResizeKeys._move_w KP_Right = MoveResizeKeys._move_e KP_Up = MoveResizeKeys._move_n KP_Down = MoveResizeKeys._move_s KP_Home = MoveResizeKeys._move_nw KP_End = MoveResizeKeys._move_sw KP_Prior = MoveResizeKeys._move_ne KP_Next = MoveResizeKeys._move_se S_KP_Left = MoveResizeKeys._enlarge_w S_KP_Right = MoveResizeKeys._enlarge_e S_KP_Up = MoveResizeKeys._enlarge_n S_KP_Down = MoveResizeKeys._enlarge_s S_KP_Home = MoveResizeKeys._enlarge_nw S_KP_End = MoveResizeKeys._enlarge_sw S_KP_Prior = MoveResizeKeys._enlarge_ne S_KP_Next = MoveResizeKeys._enlarge_se C_KP_Left = MoveResizeKeys._shrink_w C_KP_Right = MoveResizeKeys._shrink_e C_KP_Up = MoveResizeKeys._shrink_n C_KP_Down = MoveResizeKeys._shrink_s C_KP_Home = MoveResizeKeys._shrink_nw C_KP_End = MoveResizeKeys._shrink_sw C_KP_Prior = MoveResizeKeys._shrink_ne C_KP_Next = MoveResizeKeys._shrink_se KP_Begin = MoveResizeKeys._moveresize_end S_KP_Begin = MoveResizeKeys._moveresize_end C_KP_Begin = MoveResizeKeys._moveresize_end Escape = MoveResizeKeys._moveresize_abort KP_Delete = MoveResizeKeys._moveresize_abort def KP_Add(self, evt): self.mv.client.iconify() self.wm.current_screen.view_next() self.mv.client.moveresize(self.mv.x, self.mv.y, self.mv.width, self.mv.height) self.mv.client.deiconify() self.mv.client.activate() # Sync and give underlying windows half a second to redraw themselves self.wm.display.Sync(0) time.sleep(1) self.mv.do() class CycleUMKeys(keys.KeyGrabKeyboard): propagate_keys = 0 timeout = 10 def __init__(self, wm, dispatch, time): keys.KeyGrabKeyboard.__init__(self, wm, dispatch, time) self.cy = cycle.CycleUnmapped(wm.current_screen, 1) def Tab(self, event): self.cy.next() C_Tab = Tab def S_Tab(self, event): self.cy.previous() def Return(self, event): wmanager.debug('keys', 'Escaping CycleMapped mode') self._cleanup() self.cy.end() def Escape(self, event): wmanager.debug('keys', 'Aborting CycleMapped mode') self._cleanup() self.cy.abort() _timeout = Escape # Support for using Cyclops to detect circular references # There are two kinds of circular references in PLWM: static and # dynamic. The static chains are created at startup, and contains # the WindowManager object, all the Screen objects and the basic # keyhandler. # The dynamic chains are primarily the Client objects and temporary # KeyHandlers. These objects have several circular references which # must be broken when the object isn't needed any longer. If these chains # aren't broken by properly destroying the objects (e.g. by calling # Client.withdraw or KeyHandler._cleanup) they will be left in memory, # causing a memory leak. # The dynamic chains are the problem, and new chains must be found to fix # memory problems. This makes using Cyclops slightly difficult: we must # filter out the static chains to be able to focus on the dynamic chains. # We have currently two different ways to find out which chains # to ignore, hopefully they can complement each other in finding new # chains. # Method 1: # # Use Cyclops to find all objects created at startup. # Then filter out all cycles involving these objects. class cycle_filter1: def __init__(self, rootset): self.rootobjs = {} for rc, cyclic, obj in rootset: if rc != 0: self.rootobjs[id(obj)] = 1 print 'Length of rootset', len(rootset) print 'Length of rootobjs', len(self.rootobjs) # Ignore cycles which include objects in the startup rootset def __call__(self, cycle): for obj, index in cycle: if self.rootobjs.has_key(id(obj)): return 0 return 1 def cycle_detect1(): import Cyclops z = Cyclops.CycleFinder() # Create PLWM inside cyclop to find all objects created at startup p = z.run(PLWM) # Get the rootset (== all objects) and reset the cyclop rootset = z.get_rootset() z.clear() # Add a filter for the rootset z.install_cycle_filter(cycle_filter1(rootset)) # Finally, run the loop z.run(p.brave_loop) z.find_cycles() # Print info z.show_stats() z.show_cycles() z.show_arcs() # Method 2: # # Ask the WindowManager about all dynamic objects it added during startup, # and then filter out static objects when finding cycles. def cycle_filter2(cycle): for obj, index in cycle: if isinstance(obj, wmanager.WindowManager) or \ isinstance(obj, wmanager.Screen) or \ isinstance(obj, BasicKeys): return 0 return 1 def cycle_detect2(): import Cyclops z = Cyclops.CycleFinder() # Create the window manager and find its dynamic cycle roots p = PLWM() p._register_cycle_roots(z) # Run the event handler z.run(p.brave_loop) # Tell the window manager to do cleanup of all cycle roots p._cleanup_cycle_roots() # We must handle all the events generated by the cleanup p.handle_events() z.install_cycle_filter(cycle_filter2) # Purge relly dead roots, as this will include (hopefully) all # dynamically created objects with no circular references z.find_cycles(1) # Print info z.show_stats() z.show_cycles() z.show_arcs() if __name__ == '__main__': sync = 0 while len(sys.argv) > 1: if sys.argv[1] == '-d': wmanager.debug = wmanager.do_debug elif sys.argv[1] == '-s': sync = 1 elif sys.argv[1] == '-c': cycle_detect2() sys.exit(0) del sys.argv[1] try: p = PLWM() except wmanager.error_no_unmanaged_screens: sys.stderr.write(sys.argv[0] + ': Another window manager already running?\n') sys.exit(1) if sync: p.display.Synchronize(1) p.brave_loop()