Advertisement
Guest User

Compiz Scale Minimized Window Fix

a guest
Nov 20th, 2016
432
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 15.92 KB | None | 0 0
  1. #!/usr/bin/env python
  2.  
  3. # Updated by Fadi R
  4. # November 2016
  5.  
  6. # Written by: Greg Till
  7. # With credit to: ryanhaigh, Raz Ziv
  8. # October 2009
  9. # Public domain software
  10.  
  11. from __future__ import division
  12.  
  13.  
  14. import gi
  15. gi.require_version('Gtk', '3.0')
  16. from gi.repository import Gtk
  17.  
  18. import os
  19. gi.require_version('Wnck', '3.0')
  20. from gi.repository import Wnck
  21. import time
  22. import subprocess
  23. import ctypes
  24.  
  25.  
  26. """scale.py: Unminimizes and re-minimizes windows for Compiz's Scale plugin
  27.  
  28. More specifically:
  29.  
  30. - Unminimize all minimized viewport windows
  31. - Launch the Scale plugin of the Compiz window manager
  32. - Re-minimize previously minimized windows (other than any newly activated
  33.    window and windows closed through Scale)
  34.    
  35. If you set this script up with a hot corner in Compiz using the Command plugin
  36.    and attempt to activate the script using the hot corner *while a previously
  37.    activated instance of the script is already running*, you have several
  38.    possible behaviors to choose from, using the 'hotCornerOption' variable.
  39.    
  40.    [0] (Default) First instance runs Scale with unminimizing windows. Second
  41.    instance does nothing. (In other words, it continues to wait for user
  42.    action to exit the first instance of Scale w/unminimized windows.)
  43.    
  44.    [1] The instances alternate between Scale both with and without
  45.    unminimizing windows, the first instance being Scale *without* unminimizing
  46.    windows. (However, if there are no previously unminimized windows to choose
  47.    from, the script unminimizes all windows.)
  48.    
  49.    [2] The instances alternate between Scale both with and without
  50.    unminimizing windows, the first instance being Scale *with* unminimizing
  51.    windows.
  52.    
  53. In this script you also may customize the 'pluginDelay' variable. This
  54.    variable refers to the duration between when Scale activates and when you
  55.    may select a window using Scale. In rare instances, windows may
  56.    spontaneously become active after they have been unminimized and after
  57.    Scale has started, but before you have selected a window in Scale. A change
  58.    in window status will trigger the reminimization process. By setting a
  59.    short delay after Scale begins, these rare active window changes will be
  60.    caught and ignored. Each such active window change will reset the time.
  61.    Typically, it can be a small number (a fraction of a second) and is needed,
  62.    if at all, only when the system is working under heavy load.
  63.    
  64. Scale can show the windows in the current viewport or all viewports. To have
  65.    Scale show windows from the current viewport (the default), the command
  66.    near the end of this script that begins 'org/freedesktop/compiz...',
  67.    should read as follows:
  68.    
  69.        '/org/freedesktop/compiz/scale/allscreens/initiate_key'
  70.        
  71. To have Scale show windows from all viewports, change the command as follows:
  72.    
  73.        '/org/freedesktop/compiz/scale/allscreens/initiate_all_key'
  74.        
  75. Regardless of which approach you use, only the windows in the current viewport
  76.    are (un)minimized. To (un)minimize windows from all viewports would be a
  77.    dizzying experience due to the rapid viewport changing it would
  78.    require.
  79.    
  80. Finally, some Netbook Remix users may be unable to use this script out of the
  81.    box. For these users, triggerring the script will often cause all windows
  82.    to be immediately minimized. These users will need to uncomment the
  83.    'blackList' variable listed below. They may also need to 'sticky' a
  84.    window (and preferrably make it reside under other windows) to achieve
  85.    full functionality. See this thread:
  86.    ubuntuforums.org/showthread.php?t=976002 , especially beginning with post
  87.    #18."""
  88.    
  89. previousTime = 0
  90.  
  91. def get_screen():
  92.     """Get the screen object and refresh the screen"""
  93.     screen = Wnck.Screen.get_default()
  94.     screen.force_update()
  95.     return screen
  96.  
  97. def get_windows():
  98.     """Get the list of all windows"""
  99.     screen = get_screen()
  100.     allWindows = screen.get_windows_stacked()
  101.     return allWindows
  102.            
  103. def get_tasklist_windows():
  104.     """Get the list of windows in the tasklist"""
  105.     # The tasklist can be configured to include windows from other viewports
  106.     allWindows = get_windows()
  107.     tasklistWindows = []
  108.     for window in allWindows:
  109.         if window.is_skip_tasklist():
  110.             continue
  111.         elif window.is_skip_pager():
  112.             continue
  113.         elif window.is_sticky():
  114.             continue
  115.         else:
  116.             tasklistWindows.append(window)
  117.     return tasklistWindows
  118.  
  119. def get_viewport_windows():
  120.     """Get the list of windows that are in the viewport"""
  121.     allWindows = get_windows()
  122.     screen = get_screen()
  123.     workspace = screen.get_active_workspace()
  124.     viewportWindows = []
  125.     for window in allWindows:
  126.         if window.is_in_viewport(workspace):
  127.             viewportWindows.append(window)
  128.     return viewportWindows
  129.  
  130. def get_eligible_windows():
  131.     """Get the list of windows that are in the viewport and the tasklist"""
  132.     # Only these windows are subject to being unminimized
  133.     eligibleWindows = []
  134.     allWindows = get_windows()
  135.     tasklistWindows = get_tasklist_windows()
  136.     viewportWindows = get_viewport_windows()
  137.     for window in allWindows:
  138.         if window in tasklistWindows and window in viewportWindows:
  139.             eligibleWindows.append(window)
  140.     return eligibleWindows
  141.            
  142. def get_ineligible_windows():
  143.     """Get the list of windows that are in the viewport & not the tasklist"""
  144.     # One of these windows will be activated later in the script
  145.     ineligibleWindows = []
  146.     allWindows = get_windows()
  147.     viewportWindows = get_viewport_windows()
  148.     eligibleWindows = get_eligible_windows()
  149.     activeWindow = get_active_window()
  150.     blackList = []
  151.     # ------------------------------------------------------------------------
  152.     # Uncomment and update this line as desired according to instructions at
  153.     #    the top of this script
  154.     # blackList = ['Home','Top Expanded Edge Panel']
  155.     # ------------------------------------------------------------------------
  156.     for window in allWindows:
  157.         if window in eligibleWindows:
  158.              continue
  159.         elif window.get_name() in blackList:
  160.             continue
  161.         # Don't let the Desktop be included in the list of ineligibleWindows if
  162.         #    it is currently the active window
  163.         # Cannot refer to the Desktop by name because not every WM will list it
  164.         elif window in viewportWindows and window != activeWindow:
  165.             ineligibleWindows.append(window)
  166.     return ineligibleWindows  
  167.  
  168. def get_active_window():
  169.     """Get the active window"""
  170.     # After its initial invoation, the 'screen.get_active_window()' call will
  171.     #    not update again until 'screen.connect()' is called. To check on
  172.     #    any window activation changes mid-script, use a call to xprop, such
  173.     #    as: 'xprop -root | grep "_NET_ACTIVE_WINDOW(WINDOW)"| cut -d " " -f 5'
  174.     screen = get_screen()
  175.     activeWindow = screen.get_active_window()
  176.     return activeWindow
  177.  
  178. def determine_minimized(windows):
  179.     """Determine which windows in a given list are minimized"""
  180.     minimizedWindows = []
  181.     for window in windows:
  182.         if window.is_minimized():
  183.             minimizedWindows.append(window)
  184.     return minimizedWindows
  185.  
  186. def instance_count():
  187.     """Determine number of instances running"""
  188.     # Should always be at least one
  189.     libc = ctypes.CDLL('libc.so.6')
  190.     # Set process name so it isn't 'Python' by default
  191.     processName = "ScalePython"
  192.     libc.prctl(15, processName, 0, 0, 0)
  193.     # Get process list
  194.     output = subprocess.Popen('ps -A | grep ' + processName, shell=True,
  195.         stdin=subprocess.PIPE, stdout=subprocess.PIPE)
  196.     output = output.communicate()[0]
  197.     splitOutput = output.split()
  198.     return splitOutput.count(processName)
  199.    
  200. def do_unmin():
  201.     """Determine whether to unminimize windows"""
  202.     # In some instances involving hot corners, on first run it may be desired
  203.     #    to run Scale without unminimizing windows
  204.     # ------------------------------------------------------------------------
  205.     # Update this line as desired according to instructions at the top of this
  206.     #    script (enter 0, 1, or 2)
  207.     hotCornerOption = 0
  208.     # ------------------------------------------------------------------------    
  209.     instanceCount = instance_count()      
  210.     if instanceCount >= 2:
  211.         if hotCornerOption == 0:
  212.             Gtk.main_quit()
  213.         else:
  214.             return True
  215.     else:
  216.         if hotCornerOption == 0 or hotCornerOption == 2:
  217.             return True
  218.         else:
  219.             return False
  220.  
  221. def window_command(window, command):
  222.     """Handle various window actions"""
  223.     # Use wmctrl instead of equivilant commands in xwit and Wnck (the latter
  224.     #    two can cause the tasklist to blink and Wnck requires X server
  225.     #    timestamps for some actions)
  226.     # Use xwit for minimizing because wmctrl does not have a
  227.     #    working minimize function
  228.     if command == 'close':
  229.         subprocess.call('wmctrl -ic ' + str(window.get_xid()), shell=True)
  230.     if command == 'maximize_toggle':
  231.         subprocess.call('wmctrl -ir ' + str(window.get_xid()) +
  232.             ' -b toggle,maximized_vert,maximized_horz', shell=True)
  233.     if command == 'minimize':
  234.         subprocess.call('xwit -iconify -id ' + str(window.get_xid()),
  235.         shell=True)
  236.     if command == 'unminimize' or command == 'activate':
  237.         subprocess.call('wmctrl -ia ' + str(window.get_xid()), shell=True)
  238.     return
  239.  
  240. def reminimize(minimizedWindows):
  241.     # Need a new list of eligible windows because one or more windows may
  242.     #    have been closed by Scale
  243.         activeWindow = get_active_window()
  244.         newEligibleWindows = get_eligible_windows()
  245.         for window in newEligibleWindows:
  246.             if window == activeWindow:
  247.                 continue
  248.             elif window in minimizedWindows:
  249.                 window_command(window, 'minimize')
  250.         Gtk.main_quit()
  251.  
  252. def handler_reminimize(screen, window, firstIneligibleWin,
  253.     minimizedWindows):
  254.     """Re-minimize previously minimized windows after Scale finishes"""
  255.     # 'active_window_changed' WnckScreen signal
  256.     # 'window' argument reflects previously active window, not the current one
  257.     # Window selection, window close, and show desktop actions in Scale all
  258.     #    trigger this signal
  259.     # Hitting escape in Scale yields no signal; nothing can be done by this
  260.     #    script in this instance. This script won't close and windows will
  261.     #    not reminimize until after the user activated another window
  262.     # No actions from Scale are received until Scale after has finished; also,
  263.     #    after it finishes, only the signal from its final action is received
  264.     #    (thus, multiple window closures can occur but not result in multiple
  265.     #    received signals)
  266.     # This script also triggers the signal (before invoking Scale), when it
  267.     #    activates an ineligible window
  268.     # Thus, need to check if the active window is the same as the ineligible
  269.     #    window activated by this script
  270.     # If it is, then the script will wait until a window state change is
  271.     #    caused by Scale, rather than this script (so hurry up and wait)
  272.     # If it is not, then the script will reminimize windows, unless the user
  273.     #    selects the desktop in Scale, which is addressed by a different
  274.     #    handler
  275.     # ------------------------------------------------------------------------
  276.     # Duration in seconds between when Scale activates and when you may select
  277.     #    a window using Scale. Used to catch late-breaking, spontaneous active
  278.     #    window change events. Resets with each such event
  279.     # Adjust this variable as necessary
  280.     pluginDelay = 0.3
  281.     # ------------------------------------------------------------------------
  282.     activeWindow = get_active_window()
  283.     global previousTime
  284.     currentTime = time.time()
  285.     if activeWindow == firstIneligibleWin:
  286.         # This is most likely triggered by the script itself
  287.         # Pass and await selection of a window by the user
  288.         previousTime = currentTime    
  289.     elif activeWindow in minimizedWindows:
  290.         elapsedTime = 0
  291.         if previousTime:
  292.             elapsedTime = currentTime - previousTime
  293.         if elapsedTime < pluginDelay:
  294.             # Too little time has elapsed, meaning that the window was most
  295.             #    likely made active by late mapping; reset the active window
  296.             previousTime = currentTime
  297.             if firstIneligibleWin:
  298.                 window_command(firstIneligibleWin, 'activate')
  299.         else:
  300.             # Elapsed time is long enough, so presume the window was selected
  301.             #    by the user; reminimize
  302.             reminimize(minimizedWindows)
  303.     else:
  304.         # Active window is an originally unminimized window; no mapping issue
  305.         if minimizedWindows:
  306.             reminimize(minimizedWindows)
  307.         else:
  308.             Gtk.main_quit()
  309.     return
  310.      
  311. def handler_show_desktop(screen):
  312.     # 'showing_desktop_changed' WnckScreen signal
  313.     """Quit the script if the user selects the desktop in Scale"""
  314.     screen = get_screen()
  315.     toggledDesktop = screen.get_showing_desktop()
  316.     if toggledDesktop:
  317.         Gtk.main_quit()
  318.     return
  319.            
  320. def main():
  321.     # ************************************************************************
  322.     # Unminimize all minimized viewport windows
  323.     # ************************************************************************
  324.     eligibleWindows = get_eligible_windows()
  325.     ineligibleWindows = get_ineligible_windows()
  326.     if eligibleWindows:
  327.         minimizedWindows = determine_minimized(eligibleWindows)
  328.     else:
  329.         os._exit(0)
  330.     doUnmin = do_unmin()
  331.     if doUnmin == True or minimizedWindows == eligibleWindows:
  332.         if minimizedWindows:
  333.             for window in minimizedWindows:
  334.                 window_command(window, 'unminimize')
  335.     else:    
  336.         minimizedWindows = []
  337.     # ************************************************************************
  338.     # Launch the Scale plugin of the Compiz window manager
  339.     # ************************************************************************
  340.     # Aside from ESCaping out, Scale will exit upon one of three actions:
  341.     #    Selecting a tasklist window, closing the last tasklist window, or
  342.     #    showing the desktop
  343.     # Launch Scale
  344.     scale=subprocess.call('dbus-send --type=method_call ' +
  345.         '--dest=org.freedesktop.compiz ' +
  346.     # ------------------------------------------------------------------------
  347.     # Update this line as desired according to instructions at the top of this
  348.     #    script
  349.         '/org/freedesktop/compiz/scale/allscreens/initiate_key '
  350.     # ------------------------------------------------------------------------
  351.         + ' org.freedesktop.compiz.activate string:\'root\' ' +
  352.         'int32:`xwininfo -root | grep id: | awk \'{ print $4 }\'`', shell=True)
  353.     # Activating a non-tasklist window in this script ensures that Scale will
  354.     #    always generate an 'active_window_changed' event when it exits
  355.     if ineligibleWindows:
  356.         firstIneligibleWin = ineligibleWindows[0]
  357.         window_command(firstIneligibleWin, 'activate')
  358.     else:
  359.     #   In this case, the active window will be the last window unminimized
  360.         firstIneligibleWin = ""
  361.     # ************************************************************************
  362.     # Re-minimize previously minimized windows (other than any newly activated
  363.     #    window and windows closed through Scale)
  364.     # ************************************************************************
  365.     screen = get_screen()
  366.     screen.connect('active_window_changed', handler_reminimize,
  367.         firstIneligibleWin, minimizedWindows)
  368.     screen.connect('showing_desktop_changed', handler_show_desktop)
  369.     Gtk.main()
  370.  
  371. if __name__ == '__main__':
  372.     main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement