mirror of
https://github.com/gryf/moveto.git
synced 2025-12-17 11:30:21 +01:00
Use gdk to dedect screen layout
This commit is contained in:
161
res_pos_win.py
161
res_pos_win.py
@@ -6,7 +6,7 @@ Script calculates size of the target windows depending on current screen size.
|
|||||||
Required python 2.7
|
Required python 2.7
|
||||||
|
|
||||||
Dependencies:
|
Dependencies:
|
||||||
- xrandr
|
- pygtk
|
||||||
- wmctrl
|
- wmctrl
|
||||||
- xdotool
|
- xdotool
|
||||||
- xwininfo
|
- xwininfo
|
||||||
@@ -47,17 +47,20 @@ possible moves of the depicted window would be:
|
|||||||
- move to screen 1 to the left half
|
- move to screen 1 to the left half
|
||||||
- move to screen 1 to the right half
|
- move to screen 1 to the right half
|
||||||
|
|
||||||
|
TODO: Make it more flexible with different window configurations
|
||||||
|
|
||||||
Author: Roman 'gryf' Dobosz <gryf73@gmail.com>
|
Author: Roman 'gryf' Dobosz <gryf73@gmail.com>
|
||||||
Date: 2013-01-06
|
Date: 2013-01-06
|
||||||
Version: 1.1
|
Date: 2014-03-31 (used pygtk instead of xrandr, which is faster)
|
||||||
|
Version: 1.2
|
||||||
"""
|
"""
|
||||||
import os
|
|
||||||
import sys
|
import sys
|
||||||
import re
|
import re
|
||||||
from subprocess import Popen, PIPE, call
|
from subprocess import Popen, PIPE, call
|
||||||
import json
|
|
||||||
|
|
||||||
CACHE_FILENAME = "~/.cache/res_pos_win.cache"
|
from gtk import gdk
|
||||||
|
|
||||||
|
|
||||||
DOCK_ON_RIGHT = True
|
DOCK_ON_RIGHT = True
|
||||||
COVER_MINIWINDOWS = False
|
COVER_MINIWINDOWS = False
|
||||||
COVER_DOCK = False
|
COVER_DOCK = False
|
||||||
@@ -72,25 +75,12 @@ class Screens(object):
|
|||||||
"""Class container for a Screen objects and whole area coordinates"""
|
"""Class container for a Screen objects and whole area coordinates"""
|
||||||
self.screens = []
|
self.screens = []
|
||||||
self.coords = ()
|
self.coords = ()
|
||||||
|
self.dimension = None
|
||||||
|
|
||||||
def append(self, screen):
|
def append(self, screen):
|
||||||
"""Add screen"""
|
"""Add screen"""
|
||||||
self.screens.append(screen)
|
self.screens.append(screen)
|
||||||
|
|
||||||
def dump(self):
|
|
||||||
"""Dump object as dictionary - suitable for serialziation"""
|
|
||||||
return {"coords": self.coords,
|
|
||||||
"screens": [scr.dump() for scr in self.screens]}
|
|
||||||
|
|
||||||
def load(self, data):
|
|
||||||
"""Recreate object state from provided dictionary"""
|
|
||||||
self.screens = []
|
|
||||||
self.coords = tuple(data["coords"])
|
|
||||||
for scr in data['screens']:
|
|
||||||
screen = Screen()
|
|
||||||
screen.load(scr)
|
|
||||||
self.screens.append(screen)
|
|
||||||
|
|
||||||
def guess_dimensions(self, window):
|
def guess_dimensions(self, window):
|
||||||
"""
|
"""
|
||||||
Check wheter current window is in one of three states: maximized,
|
Check wheter current window is in one of three states: maximized,
|
||||||
@@ -111,6 +101,7 @@ class Screens(object):
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class Screen(object):
|
class Screen(object):
|
||||||
"""
|
"""
|
||||||
Holds separate display information. It can be separate X screen or just a
|
Holds separate display information. It can be separate X screen or just a
|
||||||
@@ -150,7 +141,6 @@ class Screen(object):
|
|||||||
# it is rare, but hell, shit happens
|
# it is rare, but hell, shit happens
|
||||||
sx = sx - 1
|
sx = sx - 1
|
||||||
|
|
||||||
|
|
||||||
if self.main and not COVER_DOCK:
|
if self.main and not COVER_DOCK:
|
||||||
# dock on the right side + 2px for border
|
# dock on the right side + 2px for border
|
||||||
self.x = sx = sx - (64 + 2)
|
self.x = sx = sx - (64 + 2)
|
||||||
@@ -169,31 +159,6 @@ class Screen(object):
|
|||||||
self.maximized['size_y'] = self.right_half['size_y'] = \
|
self.maximized['size_y'] = self.right_half['size_y'] = \
|
||||||
self.left_half['size_y'] = sy - DECOTATORS_HEIGHT
|
self.left_half['size_y'] = sy - DECOTATORS_HEIGHT
|
||||||
|
|
||||||
def dump(self):
|
|
||||||
"""
|
|
||||||
Return current Screen object state as dictionary suitable for
|
|
||||||
serialization
|
|
||||||
"""
|
|
||||||
return {"main": self.main,
|
|
||||||
"x": self.x,
|
|
||||||
"y": self.y,
|
|
||||||
"x_shift": self.x_shift,
|
|
||||||
"y_shift": self.y_shift,
|
|
||||||
"left_half": self.left_half,
|
|
||||||
"right_half": self.right_half,
|
|
||||||
"maximized": self.maximized}
|
|
||||||
|
|
||||||
def load(self, data):
|
|
||||||
"""Restore Screen state from provided dictionary"""
|
|
||||||
self.x = data['x']
|
|
||||||
self.y = data['y']
|
|
||||||
self.x_shift = data['x_shift']
|
|
||||||
self.y_shift = data['y_shift']
|
|
||||||
self.left_half = data['left_half']
|
|
||||||
self.right_half = data['right_half']
|
|
||||||
self.maximized = data['maximized']
|
|
||||||
self.main = data['main']
|
|
||||||
|
|
||||||
|
|
||||||
class WMWindow(object):
|
class WMWindow(object):
|
||||||
"""
|
"""
|
||||||
@@ -201,7 +166,7 @@ class WMWindow(object):
|
|||||||
surrounded environment (screens and such).
|
surrounded environment (screens and such).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
screen_re = re.compile("[^,]*,\scurrent\s(\d+)\sx\s(\d+),.*")
|
screen_re = re.compile("[^,].*,\scurrent\s(\d+)\sx\s(\d+),.*")
|
||||||
device_re = re.compile(".*\sconnected\s(\d+)x(\d+)\+(\d+)\+(\d+)")
|
device_re = re.compile(".*\sconnected\s(\d+)x(\d+)\+(\d+)\+(\d+)")
|
||||||
display_re = re.compile("^.*,\scurrent\s(\d+)\sx\s(\d+),\s.*$")
|
display_re = re.compile("^.*,\scurrent\s(\d+)\sx\s(\d+),\s.*$")
|
||||||
position_re = re.compile("^\s+Position:\s(\d+),(\d+)\s.*$")
|
position_re = re.compile("^\s+Position:\s(\d+),(\d+)\s.*$")
|
||||||
@@ -220,22 +185,9 @@ class WMWindow(object):
|
|||||||
self.current_screen = 0
|
self.current_screen = 0
|
||||||
self.state = None
|
self.state = None
|
||||||
|
|
||||||
self._load_screens()
|
self._discover_screens()
|
||||||
self._get_props()
|
self._get_props()
|
||||||
|
|
||||||
def _load_screens(self):
|
|
||||||
"""
|
|
||||||
Load screen form cache file or create them from scratch if cache
|
|
||||||
doesn't exists
|
|
||||||
"""
|
|
||||||
fname = os.path.expanduser(CACHE_FILENAME)
|
|
||||||
if not os.path.exists(fname):
|
|
||||||
self._discover_screens()
|
|
||||||
else:
|
|
||||||
with open(fname) as fp_:
|
|
||||||
self.screens = Screens()
|
|
||||||
self.screens.load(json.load(fp_))
|
|
||||||
|
|
||||||
def _get_props(self):
|
def _get_props(self):
|
||||||
"""
|
"""
|
||||||
Update current window dimensions and position
|
Update current window dimensions and position
|
||||||
@@ -281,45 +233,20 @@ class WMWindow(object):
|
|||||||
def _discover_screens(self):
|
def _discover_screens(self):
|
||||||
"""Create list of available screens. Assuming, that first screen
|
"""Create list of available screens. Assuming, that first screen
|
||||||
reported is main screen."""
|
reported is main screen."""
|
||||||
out = Popen(['xrandr'], stdout=PIPE).communicate()[0]
|
|
||||||
self.screens = Screens()
|
self.screens = Screens()
|
||||||
|
|
||||||
for line in out.split("\n"):
|
# get the screen dimension - it may be composed by several outputs
|
||||||
if self.device_re.match(line):
|
self.display_size = (gdk.screen_width(), gdk.screen_height())
|
||||||
x, y, sx, sy = self.device_re.match(line).groups()
|
|
||||||
screen = Screen(x, y, sx, sy)
|
|
||||||
if not self.screens.screens:
|
|
||||||
screen.main = True
|
|
||||||
screen.calculate_columns()
|
|
||||||
self.screens.append(screen)
|
|
||||||
match = self.display_re.match(line)
|
|
||||||
if match:
|
|
||||||
self.display_size = match.groups()
|
|
||||||
|
|
||||||
fname = os.path.expanduser(CACHE_FILENAME)
|
gdk_screen = gdk.screen_get_default()
|
||||||
|
for out_num in range(gdk_screen.get_n_monitors()):
|
||||||
# sort screens depending on the position (only horizontal order is
|
sx, sy, x, y = gdk_screen.get_monitor_geometry(out_num)
|
||||||
# supported)
|
screen = Screen(x, y, sx, sy)
|
||||||
screens = {}
|
if not self.screens.screens:
|
||||||
|
screen.main = True
|
||||||
for screen in self.screens.screens:
|
screen.calculate_columns()
|
||||||
screens[screen.x_shift] = screen
|
self.screens.append(screen)
|
||||||
|
|
||||||
self.screens.screens = [screens[key] for key in
|
|
||||||
sorted(screens.keys())]
|
|
||||||
|
|
||||||
|
|
||||||
if not os.path.exists(fname):
|
|
||||||
try:
|
|
||||||
os.makedirs(os.path.dirname(fname))
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
try:
|
|
||||||
with open(fname, "w") as fp:
|
|
||||||
json.dump(self.screens.dump(), fp)
|
|
||||||
except (OSError, IOError) as msg:
|
|
||||||
print "Error in creating cache file:\n", msg
|
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
"""Return current window coordinates and size"""
|
"""Return current window coordinates and size"""
|
||||||
@@ -350,30 +277,27 @@ class WMWindow(object):
|
|||||||
self.current_screen = idx
|
self.current_screen = idx
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def full(self):
|
# def left(self, screen_direction=None):
|
||||||
"""Maximize window"""
|
# """Maximize to left half"""
|
||||||
|
|
||||||
def left(self, screen_direction=None):
|
# coords = self.screens.screens[self.current_screen].left_half
|
||||||
"""Maximize to left half"""
|
# cmd = ['xdotool', "getactivewindow",
|
||||||
|
# "windowmove", str(coords['pos_x']), str(coords['pos_y']),
|
||||||
|
# "windowsize", str(coords['size_x']), str(coords['size_y'])]
|
||||||
|
# call(cmd)
|
||||||
|
|
||||||
coords = self.screens.screens[self.current_screen].left_half
|
# def right(self, screen_direction=None):
|
||||||
cmd = ['xdotool', "getactivewindow",
|
# """Maximize to right half"""
|
||||||
"windowmove", str(coords['pos_x']), str(coords['pos_y']),
|
# cmd = ['xdotool', "getactivewindow"]
|
||||||
"windowsize", str(coords['size_x']), str(coords['size_y'])]
|
# if screen_direction:
|
||||||
call(cmd)
|
# if not self.move_to_screen(screen_direction):
|
||||||
|
# return
|
||||||
|
|
||||||
def right(self, screen_direction=None):
|
# if screen_direction:
|
||||||
"""Maximize to right half"""
|
# move = self.move_to_screen(screen_direction)
|
||||||
cmd = ['xdotool', "getactivewindow"]
|
# if not move:
|
||||||
if screen_direction:
|
# return
|
||||||
if not self.move_to_screen(screen_direction):
|
# cmd.extend(move)
|
||||||
return
|
|
||||||
|
|
||||||
if screen_direction:
|
|
||||||
move = self.move_to_screen(screen_direction)
|
|
||||||
if not move:
|
|
||||||
return
|
|
||||||
cmd.extend(move)
|
|
||||||
|
|
||||||
def get_coords(self, which):
|
def get_coords(self, which):
|
||||||
"""Return screen coordinates"""
|
"""Return screen coordinates"""
|
||||||
@@ -422,6 +346,7 @@ def usage():
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
if len(sys.argv) < 2:
|
if len(sys.argv) < 2:
|
||||||
usage()
|
usage()
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
if sys.argv[1] == "cycle_left":
|
if sys.argv[1] == "cycle_left":
|
||||||
cycle("left")
|
cycle("left")
|
||||||
@@ -429,4 +354,4 @@ if __name__ == "__main__":
|
|||||||
cycle("right")
|
cycle("right")
|
||||||
else:
|
else:
|
||||||
usage()
|
usage()
|
||||||
|
sys.exit(1)
|
||||||
|
|||||||
Reference in New Issue
Block a user