def test_drag_resize_floating_client(hlwm, x11, mouse, live_update): hlwm.attr.settings.update_dragged_clients = hlwm.bool(live_update) client, winid = x11.create_client(geometry=(50, 50, 300, 200)) hlwm.call(f'set_attr clients.{winid}.floating true') geom_before = Rectangle.from_user_str(hlwm.attr.clients[winid].geometry_reported()) assert geom_before == x11.get_absolute_geometry(client) # duck-typing assert (geom_before.width, geom_before.height) == (300, 200) # move cursor to the top left corner, so we change the # window position and the size (and the bottom right corner is fixed) # Just positioning the mouse pointer, no need to wait for hlwm mouse.move_into(winid, x=0, y=0, wait=False) hlwm.call(['drag', winid, 'resize']) assert hlwm.get_attr('clients.dragged.winid') == winid mouse.move_relative(100, 120) final_size = (geom_before.width - 100, geom_before.height - 120) # check geometry during drag x11.display.sync() geom_after = client.get_geometry() x_after, y_after = x11.get_absolute_top_left(client) assert (x_after, y_after) == (geom_before.x + 100, geom_before.y + 120) expected_size = (geom_before.width, geom_before.height) if live_update: expected_size = final_size assert (geom_after.width, geom_after.height) == expected_size # stop drag and check final size mouse.click('1', wait=True) geom_after = client.get_geometry() assert (geom_after.width, geom_after.height) == final_size assert Rectangle.from_user_str(hlwm.attr.clients[winid].geometry_reported()) \ == geom_before.adjusted(dx=100, dy=120, dw=-100, dh=-120)
def test_sizehint_change_applied(hlwm, x11, floating): hlwm.attr.tags.focus.floating = floating # create a client with a geometry that does not match the sizehints # we will set later handle, winid = x11.create_client() old_geometry = Rectangle(x=30, y=40, width=111, height=121) hlwm.attr.clients[winid].floating_geometry = old_geometry if floating: assert hlwm.attr.clients[winid].content_geometry().size( ) == old_geometry.size() # update the size hints: hints = { 'flags': Xutil.PResizeInc, 'width_inc': 5, 'height_inc': 7, } handle.set_wm_normal_hints(hints) x11.sync_with_hlwm() new_geometry = hlwm.attr.clients[winid].floating_geometry() # the new size adheres to the size hints assert new_geometry.width % hints['width_inc'] == 0 assert new_geometry.height % hints['height_inc'] == 0 # the new size changes only as little as necessary assert abs(new_geometry.width - old_geometry.width) <= hints['width_inc'] assert abs(new_geometry.height - old_geometry.height) <= hints['height_inc'] if floating: assert hlwm.attr.clients[winid].content_geometry().size( ) == new_geometry.size()
def test_floating_geometry_and_placement(hlwm, x11, place_center): hlwm.attr.tags.focus.floating = True rule_geo = Rectangle(30, 40, 140, 170) rule = ['floating_geometry=' + rule_geo.to_user_str()] if place_center: rule += ['floatplacement=center'] else: # also add another rule whose floatplacement is then overwritten: hlwm.call('rule floatplacement=center') rule += ['floatplacement=none'] # 'floatplacement' is only applied by 'rule', so no test # for 'apply_tmp_rule' or 'apply_rules' hlwm.call(['rule'] + rule) _, winid = x11.create_client() client_geo = hlwm.attr.clients[winid].floating_geometry() monitor_geo = hlwm.attr.monitors.focus.geometry() assert client_geo.size() == rule_geo.size() if place_center: assert client_geo.center() == monitor_geo.center() else: assert client_geo.topleft() == rule_geo.topleft()
def test_floating_geometry(hlwm, x11, command, visible_tag): if command != 'rule': _, winid = x11.create_client() # client first geo = Rectangle(30, 40, 140, 170) rule = [ 'floating=on', 'focus=on', 'floating_geometry=' + geo.to_user_str() ] if not visible_tag: hlwm.call('add othertag') rule += ['tag=othertag'] if command == 'rule': hlwm.call(['rule'] + rule) _, winid = x11.create_client() # client second elif command == 'apply_rules': # apply fresh rules to long existing client hlwm.call(['rule'] + rule) hlwm.call(['apply_rules', winid]) else: hlwm.call(['apply_tmp_rule', winid] + rule) if not visible_tag: hlwm.call('use othertag') assert hlwm.attr.clients.focus.floating_geometry() == geo assert hlwm.attr.clients.focus.content_geometry() == geo
def test_implicit_type_conversion_rectangle(hlwm): # TODO: change as soon as custom attributes support Rectangle! geo = Rectangle(10, 20, 400, 500) hlwm.attr.monitors.focus.geometry = geo assert hlwm.attr.monitors.focus.geometry() == geo geo = Rectangle(20, 30, 422, 522) hlwm.attr.monitors.focus.geometry = geo assert hlwm.attr.monitors.focus.geometry() == geo
def test_directional_shift_resize_left_up(hlwm, command, direction, put_obstacle): """ run: shift/resize up/left possibly put a window obstacle in the way """ hlwm.attr.tags.focus.floating = True client, _ = hlwm.create_client() bw = 3 hlwm.attr.theme.border_width = bw gap = 2 hlwm.attr.settings.snap_gap = gap hlwm.attr.clients[client].sizehints_floating = False old_geo = Rectangle(x=160, y=170, width=80, height=90) hlwm.attr.clients[client].floating_geometry = old_geo # possibly put a window obstacle between the 'client' and the screen edge: if put_obstacle: obstacle, _ = hlwm.create_client() hlwm.attr.clients[obstacle].sizehints_floating = False obstacle_pos = 100 + bw if direction == 'up': hlwm.attr.clients[obstacle].floating_geometry = \ Rectangle(x=0, y=0, width=400, height=100) elif direction == 'left': hlwm.attr.clients[obstacle].floating_geometry = \ Rectangle(x=0, y=0, width=100, height=400) else: # the next obstacle is the screen edge obstacle_pos = 0 if direction == 'up': # expected position after shift up: same x, different y expected_pos = (160, obstacle_pos + bw + gap) elif direction == 'left': # expected position after shift left: different x, same y expected_pos = (obstacle_pos + bw + gap, 170) hlwm.call(['jumpto', client]) hlwm.call([command, direction]) # the actual shift/resize # check that the top left corner of the window is adjusted as expected new_geo = hlwm.attr.clients.focus.floating_geometry() assert expected_pos == (new_geo.x, new_geo.y) if command == 'shift': # shift means: size is unchanged assert (old_geo.width, old_geo.height) == (new_geo.width, new_geo.height) else: # resize means: bottom right corner is unchanged assert old_geo.x + old_geo.width == new_geo.x + new_geo.width assert old_geo.y + old_geo.height == new_geo.y + new_geo.height
def test_resize_shrink_not_possible(hlwm, direction): hlwm.attr.settings.snap_gap = 5 hlwm.attr.tags.focus.floating = True winid, _ = hlwm.create_client() hlwm.attr.clients.focus.sizehints_floating = False # place the client roughly in the center of the monitor: geo_orig = Rectangle(x=200, y=200, width=160, height=140) hlwm.attr.clients.focus.floating_geometry = geo_orig # call resize multiple times for i in range(0, 15): proc = hlwm.unchecked_call(['resize', direction]) if proc.returncode != 0: assert f"{winid} is too small to be shrunk" in proc.stderr break geo_final = hlwm.attr.clients.focus.floating_geometry() # after calling 'resize' that often, the minimum window size # must have been reached. So calling it again will not change the geometry: hlwm.call_xfail(['resize', direction]).expect_stderr('too small') assert geo_final == hlwm.attr.clients.focus.floating_geometry() if direction in ['up', 'down']: assert geo_orig.width == geo_final.width else: assert geo_orig.height == geo_final.height assert geo_final.width >= 50 assert geo_final.height >= 50
def test_drag_move_sends_configure(hlwm, x11, mouse, update_dragged): hlwm.attr.tags.focus.floating = 'on' hlwm.attr.settings.update_dragged_clients = hlwm.bool(update_dragged) client, winid = x11.create_client() x, y = x11.get_absolute_top_left(client) before = Rectangle.from_user_str(hlwm.attr.clients[winid].geometry_reported()) assert (x, y) == (before.x, before.y) mouse.move_into(winid, wait=True) hlwm.call(['drag', winid, 'move']) mouse.move_relative(12, 15) mouse.click('1') # stop dragging hlwm.call('true') # sync after = Rectangle.from_user_str(hlwm.attr.clients[winid].geometry_reported()) assert before.adjusted(dx=12, dy=15) == after
def test_detect_monitors_xrandr(hlwm_spawner, screens, xvfb): args = ['-extension', 'XINERAMA'] args += ['+extension', 'RANDR'] with MultiscreenDisplay(server='Xephyr', screens=screens, extra_args=args) as xserver: # boot up hlwm hlwm_proc = hlwm_spawner(display=xserver.display) hlwm = conftest.HlwmBridge(xserver.display, hlwm_proc) # for the debugging if something fails: lines = hlwm.call('detect_monitors --list-all').stdout.splitlines() for cur_line in lines: words = cur_line.split(' ') if words[0] == 'xinerama:': # check that xinerama is disabled assert len(words) == 1 elif words[0] == 'xrandr:': # unfortunately, xrandr in Xephyr only reports the first screen assert len(words) == 2 assert words[1] == Rectangle( x=0, y=0, width=screens[0][0], height=screens[0][1]).to_user_str() else: assert False, f"unknown detect_monitors output: {cur_line}" hlwm_proc.shutdown()
def test_can_move_client_at_monitor_edge(hlwm, mouse): hlwm.attr.tags.focus.floating = True winid, _ = hlwm.create_client() hlwm.call('move_monitor 0 600x400+0+0') # move floating coordinates clearly off screen: floating_geo = Rectangle(x=2000, y=4000, width=300, height=200) hlwm.attr.clients[winid].sizehints_floating = False hlwm.attr.clients[winid].floating_geometry = floating_geo # still, the window overlaps with the monitor by a few pixels content_geo = hlwm.attr.clients[winid].content_geometry() assert content_geo.x + 20 < 600 assert content_geo.y + 20 < 400 assert hlwm.attr.clients[winid].floating_geometry() == floating_geo # so, despite the floating geo is far off, # the window is shown on the monitor # now drag the window by a few pixels back into the monitor: mouse.move_into(winid) hlwm.attr.settings.update_dragged_clients = True hlwm.call(['drag', winid, 'move']) delta = Point(-72, -93) assert hlwm.get_attr('clients.dragged.winid') == winid mouse.move_relative(delta.x, delta.y) # then, we expect that the window actually moves back # by precisely this distance: # (in old hlwm versions, the window was glueing in the bottom right corner) assert hlwm.attr.clients[winid].content_geometry() == content_geo.adjusted(dx=delta.x, dy=delta.y) # but of course the floating geo changed a lot (e.g. more than 500), # because the window is now within the screen again: new_floating_geo = hlwm.attr.clients[winid].floating_geometry() assert abs(new_floating_geo.x - floating_geo.x) > 500 assert abs(new_floating_geo.y - floating_geo.y) > 500
def test_decorated_off_floating_geometry_correct(hlwm): winid, _ = hlwm.create_client() hlwm.attr.theme.border_width = 8 client_obj = hlwm.attr.clients[winid] client_obj.floating_geometry = Rectangle(10, 20, 200, 100) client_obj.floating = True client_obj.decorated = False assert client_obj.floating_geometry() == client_obj.decoration_geometry()
def test_floating_geometry_change(hlwm, minimized, floating, x11, othertag): if othertag: hlwm.call('add othertag') hlwm.call('rule tag=othertag') handle, winid = x11.create_client() clientobj = hlwm.attr.clients[winid] clientobj.floating = hlwm.bool(floating) clientobj.minimized = hlwm.bool(minimized) hlwm.call('pad "" 0 0 0 0') hlwm.call('move_monitor "" 800x600+0+0') for geom in [Rectangle(x=50, y=100, width=100, height=300), Rectangle(x=-20, y=2, width=430, height=200)]: clientobj.floating_geometry = geom.to_user_str() if not minimized and not othertag: assert (clientobj.content_geometry() == geom) == floating assert (x11.get_absolute_top_left(handle) == (geom.x, geom.y)) == floating
def test_resize_shrink_client(hlwm, direction): hlwm.attr.settings.snap_gap = 5 hlwm.attr.tags.focus.floating = True hlwm.attr.monitors.focus.geometry = Rectangle(x=0, y=0, width=800, height=900) hlwm.create_client() hlwm.attr.clients.focus.sizehints_floating = False # place the client roughly in the center of the monitor: geo_orig = Rectangle(x=200, y=200, width=300, height=400) hlwm.attr.clients.focus.floating_geometry = geo_orig # shift the client towards the monitor edge in the specified 'direction' hlwm.call(['shift', direction]) geo_moved = hlwm.attr.clients.focus.floating_geometry() # and the size is as originally assert geo_moved.width == geo_orig.width assert geo_moved.height == geo_orig.height # calling 'resize' with the same direction forces the client to shrink: hlwm.call(['resize', direction]) geo_shrunk = hlwm.attr.clients.focus.floating_geometry() if direction in ['up', 'down']: # client was shrunk in height assert geo_shrunk.width == geo_moved.width assert geo_shrunk.height == geo_moved.height // 2 else: # client was shrunk in width assert geo_shrunk.width == geo_moved.width // 2 assert geo_shrunk.height == geo_moved.height if direction in ['right', 'down']: # bottom right corner stays unchanged assert geo_shrunk.x + geo_shrunk.width == geo_moved.x + geo_moved.width assert geo_shrunk.y + geo_shrunk.height == geo_moved.y + geo_moved.height else: # top left corner stays unchanged assert geo_shrunk.x == geo_moved.x assert geo_shrunk.y == geo_moved.y
def test_panel_object(hlwm, x11): assert int(hlwm.attr.panels.count()) == 0 _, winid = x11.create_client(geometry=(1, 0, 800, 30), wm_class=('panelinst', 'panelclass'), window_type='_NET_WM_WINDOW_TYPE_DOCK') assert int(hlwm.attr.panels.count()) == 1 assert hlwm.attr.panels[winid].instance() == 'panelinst' assert hlwm.attr.panels[winid]['class']() == 'panelclass' assert hlwm.attr.panels[winid].geometry() == Rectangle(1, 0, 800, 30) assert hlwm.attr.panels[winid].winid() == winid
def test_drag_floating_to_other_monitor(hlwm, mouse, source_floating, client_floating, target_floating, client_focused): total_width = hlwm.attr.monitors.focus.geometry().width total_height = hlwm.attr.monitors.focus.geometry().height # create two monitors side by side with small gaps, pads, and some odd coordinates hlwm.call('add othertag') geo1 = Rectangle(x=10, y=20, width=total_width / 2 - 30, height=total_height) geo2 = Rectangle(x=total_width / 2, y=15, width=total_width / 2, height=total_height) hlwm.call(['set_monitors', geo1.to_user_str(), geo2.to_user_str()]) hlwm.call(['pad', '0', '11', '12', '13', '14']) hlwm.call(['pad', '1', '15', '16', '17', '18']) hlwm.attr.settings.update_dragged_clients = True hlwm.attr.tags[0].floating = source_floating hlwm.attr.tags[1].floating = target_floating # another dummy client otherclient, _ = hlwm.create_client() # put client in the middle of tag 0 hlwm.call('rule floatplacement=center') winid, _ = hlwm.create_client() if client_focused: focused_winid = winid else: focused_winid = otherclient hlwm.call(['jumpto', focused_winid]) hlwm.attr.clients[winid].floating = client_floating assert hlwm.attr.clients[winid].tag() == hlwm.attr.tags[0].name() assert hlwm.attr.clients.focus.winid() == focused_winid # start dragging content_geo_before = hlwm.attr.clients[winid].content_geometry() start = content_geo_before.center() mouse.move_to(start.x, start.y) hlwm.call(['drag', winid, 'move']) end = hlwm.attr.monitors[1].geometry().center() mouse.move_to(end.x, end.y) content_geo_after = hlwm.attr.clients[winid].content_geometry() assert end == content_geo_after.center() assert hlwm.attr.clients[winid].tag() == hlwm.attr.tags[1].name() assert hlwm.attr.clients[winid].floating_effectively() is True # check that the focused client does not change: assert hlwm.attr.clients.focus.winid() == focused_winid # check that the correct monitor is focused assert hlwm.attr.monitors.focus.tag() == hlwm.attr.clients.focus.tag() assert hlwm.attr.monitors.focus.index() == (1 if client_focused else 0) if not target_floating: # if the target tag is not floating, the client must have been # set to single-window floating assert hlwm.attr.clients[winid].floating() is True else: # if the target tag is floating, then the client's floating # state is unchanged assert hlwm.attr.clients[winid].floating() == client_floating
def test_smart_placement_within_monitor(hlwm): """ In the smart placement, the outer geometry of clients must be used. So test that even with wide decorations, the top left corner of the decoration is not off screen. """ hlwm.attr.tags.focus.floating = 'on' hlwm.call('rule floatplacement=smart') bw = 25 hlwm.attr.theme.border_width = bw hlwm.attr.settings.snap_gap = 5 # something smaller than bw winid, _ = hlwm.create_client() inner_geometry = Rectangle.from_user_str( hlwm.attr.clients[winid].geometry_reported()) # assert that the top left corner of the decoration # is still within the monitor assert inner_geometry.x - bw >= 0 assert inner_geometry.y - bw >= 0
def test_detect_monitors_deduplication(hlwm_spawner, screens, xvfb): args = ['+extension', 'XINERAMA'] args += ['-extension', 'RANDR'] # Xvfb puts all screens at position (0,0), # so we use it to verify that detect_monitors removes duplicates # while preserving the order with MultiscreenDisplay(server='Xvfb', screens=screens, extra_args=args) as xserver: # boot up hlwm hlwm_proc = hlwm_spawner(display=xserver.display) hlwm = conftest.HlwmBridge(xserver.display, hlwm_proc) # create enough tags hlwm.call('chain , add tag1 , add tag2 , add tag3') assert hlwm.get_attr('monitors.count') == '1' rects = hlwm.call(['detect_monitors', '-l']).stdout.splitlines() expected = [ Rectangle(0, 0, s[0], s[1]).to_user_str() for idx, s in enumerate(screens) if screens.index(s) == idx ] assert rects == expected hlwm_proc.shutdown()