Skip to content

Commit

Permalink
Start of swap monitor functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
johnteslade committed Mar 22, 2015
1 parent 1d772bc commit 7f3a831
Show file tree
Hide file tree
Showing 8 changed files with 201 additions and 27 deletions.
3 changes: 3 additions & 0 deletions README.md
Expand Up @@ -62,6 +62,9 @@ The following is the basic default keymap - but all avaliable keymaps are printe
Ctrl+Super+a Move window to monitor on left and maximise
Ctrl+Super+s Move window to monitor on right and maximise

Ctrl+Super+q Swap windows on current monitor and monitor to left
Ctrl+Super+w Swap windows on current monitor and monitor to right

## Configuration

Azulejo configurations are stored at `~/.azulejo.json`.
Expand Down
37 changes: 25 additions & 12 deletions azulejo/arrange_move_monitor.py
Expand Up @@ -22,6 +22,29 @@ def new_monitor(self, old_monitor, direction_left):

return new_monitor

def new_window_geometry(
self,
window_original_geometry,
old_monitor_geometry,
new_monitor
):
"""Return the geometry of the new window."""

new_monitor_geometry = \
self._screen.get_monitor_geometry(new_monitor)

# TODO deal with if the window is now too large for the new monitor
new_position = Geometry(
x=new_monitor_geometry.x +
(window_original_geometry.x - old_monitor_geometry.x),
y=new_monitor_geometry.y +
(window_original_geometry.y - old_monitor_geometry.y),
width=window_original_geometry.width,
height=window_original_geometry.height,
)

return new_position

def move_window_to_monitor(
self,
window_original_geometry,
Expand All @@ -35,18 +58,8 @@ def move_window_to_monitor(
# Do we need to move the window?
if new_monitor != old_monitor:

new_monitor_geometry = \
self._screen.get_monitor_geometry(new_monitor)

# TODO deal with if the window is now too large for the new monitor
new_position = Geometry(
x=new_monitor_geometry.x +
(window_original_geometry.x - old_monitor_geometry.x),
y=new_monitor_geometry.y +
(window_original_geometry.y - old_monitor_geometry.y),
width=window_original_geometry.width,
height=window_original_geometry.height,
)
new_position = self.new_window_geometry(
window_original_geometry, old_monitor_geometry, new_monitor)

logging.debug("Moving to monitor {} and geometry {}".format(
new_monitor, new_position))
Expand Down
70 changes: 70 additions & 0 deletions azulejo/arrange_swap_monitor.py
@@ -0,0 +1,70 @@
import logging

from .arrange_move_monitor import ArrangeMonitorBase



class ArrangeSwapMonitor(ArrangeMonitorBase):
"""Class to swap a window with another monitor.
The current window will be moved to the specified monitor and maximised.
The first window on the destination monitor will be moved to current
monitor and maximised.
"""

def do(self, params):
""" Main function that performs the arrangement """

direction = params[0]

# No action if only one monitor
if self._screen.get_number_monitors() == 1:
return

windows = self._screen.get_all_window_monitors()

# Monitor A is the one with the current active window
# Monitor B is who we are swapping with

monitor_a = self._screen.get_active_window_monitor()
monitor_b = self.new_monitor(monitor_a, (direction == "left"))

monitor_a_geometry = self._screen.get_monitor_geometry(monitor_a)
monitor_b_geometry = self._screen.get_monitor_geometry(monitor_b)

if monitor_a == monitor_b:
logging.info("Cannot move this direction")
return

print windows

logging.info("Swapping windows between monitors {} and {}".format(
monitor_a, monitor_b))

window_b_index = None

# Get index into window list for active window on Monitor B
for x, window in enumerate(windows):
if window[2] == monitor_b:
window_b_index = x
break

if window_b_index is None:
logging.info("No window found on Monitor {}".format(monitor_b))
return

# Keep if we support swapping within maximising
# window_a_new_geo = self.new_window_geometry(
# windows[0][1], monitor_a_geometry, monitor_b)
#
# window_b_new_geo = self.new_window_geometry(
# windows[window_b_index][1], monitor_b_geometry, monitor_a)

move_list = [monitor_b_geometry] + \
[None] * (window_b_index - 1) + \
[monitor_a_geometry]

print move_list

# Move windows
self._screen.move_windows(move_list, reverse=True)
2 changes: 2 additions & 0 deletions azulejo/azulejo_controller.py
Expand Up @@ -6,6 +6,7 @@
from .arrange_multiple_windows import ArrangeMultipleWindows
from .arrange_rotate import ArrangeRotate
from .arrange_single_window import ArrangeSingleWindow
from .arrange_swap_monitor import ArrangeSwapMonitor



Expand All @@ -29,6 +30,7 @@ def __init__(self, screen_obj_in):
'resize_single_window': ArrangeSingleWindow(self._screen),
'resize_windows': ArrangeMultipleWindows(self._screen),
'rotate_windows': ArrangeRotate(self._screen),
'swap_monitor': ArrangeSwapMonitor(self._screen),
}


Expand Down
44 changes: 35 additions & 9 deletions azulejo/azulejo_screen.py
Expand Up @@ -37,6 +37,17 @@ def get_all_windows():
filtered_windows.reverse()
return filtered_windows

def get_all_window_monitors(self):
"""Get all windows, geometry and monitor.
Returns list of tuple window_obj, geometry, monitor.
"""
output = []
windows = self.get_all_windows()
for win in windows:
output.append((win, self.get_window_geometry(win), self.get_window_monitor(win)))
return output

def get_monitor_geometry(self, monitor=None):
"""Return a rectangle with geometry of the specified monitor.
Expand All @@ -53,24 +64,34 @@ def get_active_window():
return wnck.screen_get_default().get_active_window()


def get_active_window_monitor(self):
def get_window_monitor(self, window):
""" Returns the monitor of the currently active window """

# Find the active window coordinates then find out which monitor this is
active_window_geo = self.get_active_window_geometry()
# Find the window coordinates then find out which monitor this is
active_window_geo = self.get_window_geometry(window)
return gtk.gdk.screen_get_default().get_monitor_at_point(
active_window_geo.x, active_window_geo.y)

def get_active_window_monitor(self):
""" Returns the monitor of the window """

def get_active_window_geometry(self):
""" Returns the geometry of the current active window """
return self.get_window_monitor(self.get_active_window())

geometry = self.get_active_window().get_geometry()
@staticmethod
def get_window_geometry(window):
""" Returns the geometry of the window """

geometry = window.get_geometry()
return Geometry(
x=geometry[0], y=geometry[1],
width=geometry[2], height=geometry[3]
)

def get_active_window_geometry(self):
""" Returns the geometry of the current active window """

return self.get_window_geometry(self.get_active_window())


def move_active_window(self, new_geometry):
""" Moves the active window """
Expand All @@ -79,13 +100,18 @@ def move_active_window(self, new_geometry):
wnck.screen_get_default().get_active_window(), new_geometry)


def move_windows(self, new_geometry_list):
def move_windows(self, new_geometry_list, reverse=False):
""" Moves a number of windows - starting from the active """

filtered_windows = self.get_all_windows()

for x in range(len(new_geometry_list)):
if x < len(filtered_windows):
if reverse:
window_indexes = range(len(new_geometry_list) - 1, 0, -1)
else:
window_indexes = range(len(new_geometry_list))

for x in window_indexes:
if x < len(filtered_windows) and new_geometry_list[x]:
self.move_window(filtered_windows[x], new_geometry_list[x])

@staticmethod
Expand Down
16 changes: 16 additions & 0 deletions azulejo/initial_config.json
Expand Up @@ -40,6 +40,22 @@
]
},

{
"name": "swap-to-monitor-left",
"description": "move window to monitor to left",
"keybind": ["<Ctrl><Super>q"],
"function": "swap_monitor",
"parameters": ["left", ""]
},

{
"name": "swap-to-monitor-right",
"description": "move window to monitor to right",
"keybind": ["<Ctrl><Super>w"],
"function": "swap_monitor",
"parameters": ["right", ""]
},

{
"name": "window-to-monitor-left",
"description": "move window to monitor to left",
Expand Down
24 changes: 18 additions & 6 deletions azulejo/test/screen_mock_base.py
Expand Up @@ -10,6 +10,17 @@ def get_all_windows(self):
""" Gets all windows in the screen """
return self.windows

def get_all_window_monitors(self):
"""Get all windows, geometry and monitor.
Returns list of tuple window_obj, geometry, monitor.
"""
output = []
for win in self.windows:
output.append((win, win['geometry'], self.get_window_monitor(win['geometry'])))
return output


def get_monitor_geometry(self, monitor=None):
"""Returns a rectangle with geometry of the specified monitor.
Expand All @@ -27,11 +38,12 @@ def get_active_window(self):

def get_active_window_monitor(self):
""" Returns the monitor of the currently active window """
return self.get_window_monitor(self.get_active_window_geometry())

for x in range(len(self.monitor_geometry)):
def get_window_monitor(self, window):
"""Return the monitor of the window."""

monitor = self.monitor_geometry[x]
window = self.get_active_window()['geometry']
for x, monitor in enumerate(self.monitor_geometry):

if (window.x >= monitor.x) \
and (window.x < monitor.x + monitor.width) \
Expand All @@ -42,7 +54,6 @@ def get_active_window_monitor(self):
# If we get here then we have a mismatch between windows and monitors
raise RuntimeError


def get_active_window_geometry(self):
""" Returns the geometry of the current active window """

Expand All @@ -55,11 +66,12 @@ def move_active_window(self, new_geometry):
self.windows[0]['geometry'] = new_geometry


def move_windows(self, new_geometry_list):
def move_windows(self, new_geometry_list, reverse=False):
""" Moves the active window the specified geometry """

for x in range(len(new_geometry_list)):
self.windows[x]['geometry'] = new_geometry_list[x]
if new_geometry_list[x]:
self.windows[x]['geometry'] = new_geometry_list[x]


def maximise_active_window(self):
Expand Down
32 changes: 32 additions & 0 deletions azulejo/test_azulejo.py
Expand Up @@ -366,4 +366,36 @@ def test_multiple_window_multi_monitor(self):
Geometry(x=301, y=51, width=100, height=50)
)

def test_swap_monitor(self):
"""Test swap monitor functionality."""

# Left swap
self.keybinding_obj.action_key('<Ctrl><Super>q')

print self.screen.get_all_windows()

self.assertEqual(
self.screen.get_all_windows()[0]['geometry'],
Geometry(x=0, y=0, width=200, height=100)
)

self.assertEqual(
self.screen.get_all_windows()[2]['geometry'],
Geometry(x=200, y=0, width=200, height=100)
)

# Right swap
self.keybinding_obj.action_key('<Ctrl><Super>w')

print self.screen.get_all_windows()

self.assertEqual(
self.screen.get_all_windows()[0]['geometry'],
Geometry(x=200, y=0, width=200, height=100)
)

self.assertEqual(
self.screen.get_all_windows()[1]['geometry'],
Geometry(x=0, y=0, width=200, height=100)
)

0 comments on commit 7f3a831

Please sign in to comment.