aboutsummaryrefslogtreecommitdiff
path: root/contrib/layout.py
blob: fd60fcf2136f59f86c347f3639fc62ba1aac9914 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#!/usr/bin/env python3
#
# Fibonacci sprial layout for river, implemented in simple python. Reading this
# code should help you get a basic understanding of how to use river-layout to
# create a basic layout generator.
#
# This depends on pywayland: https://github.com/flacjacket/pywayland/
#
# Q: Wow, this looks complicated!
# A: For simple layouts, you really only need to care about what's in the
#    layout_handle_layout_demand() function. And the rest isn't as complicated
#    as it looks.

import mmap
import time
from pywayland.client import Display
from pywayland.protocol.wayland import WlOutput
try:
    from pywayland.protocol.river_layout_v3 import RiverLayoutManagerV3
except:
    print("Your pywayland package does not have bindings for river-layout-v3.")
    print("You can add the bindings with the following command:")
    print("     python3 -m pywayland.scanner -i /usr/share/wayland/wayland.xml river-layout-v3.xml")
    print("Note that you may need root privileges if you have pywayland installed system-wide.")
    quit()

layout_manager = None
outputs = []
loop = True

def layout_handle_layout_demand(layout, view_count, usable_w, usable_h, tags, serial):
    x = 0
    y = 0
    w = usable_w
    h = usable_h
    i = 0
    while i < view_count:
        if i == view_count - 1:
            layout.push_view_dimensions(x, y, w, h, serial)
        else:
            if i % 2 == 0:
                w = int(w/2)
                if i % 4 == 2:
                    layout.push_view_dimensions(x + w, y, w, h, serial)
                else:
                    layout.push_view_dimensions(x, y, w, h, serial)
                    x += w
            else:
                h = int(h/2)
                if i % 4 == 3:
                    layout.push_view_dimensions(x, y + h, w, h, serial)
                else:
                    layout.push_view_dimensions(x, y, w, h, serial)
                    y += h

    # Committing the layout means telling the server that your code is done
    # laying out windows. Make sure you have pushed exactly the right amount of
    # view dimensions, a mismatch is a fatal protocol error.
    # 
    # You also have to provide a layout name. This is a user facing string that
    # the server can forward to status bars. You can use it to tell the user
    # which layout is currently in use. You could also add some status
    # information status information about your layout, which is what we do here.
    layout.commit(str(view_count) + " windows layout out by python", serial)

def layout_handle_namespace_in_use(layout):
    # Oh no, the namespace we choose is already used by another client! All we
    # can do now is destroy the layout object. Because we are lazy, we just
    # abort and let our cleanup mechanism destroy it. A more sophisticated
    # client could instead destroy only the one single affected layout object
    # and recover from this mishap. Writing such a client is left as an exercise
    # for the reader.
    print(f"Namespace already in use!")
    global loop
    loop = False

class Output(object):
    def __init__(self):
        self.output = None
        self.layout = None
        self.id = None

    def destroy(self):
        if self.layout != None:
            self.layout.destroy()
        if self.output != None:
            self.output.destroy()

    def configure(self):
        global layout_manager
        if self.layout == None and layout_manager != None:
            # We need to set a namespace, which is used to identify our layout.
            self.layout = layout_manager.get_layout(self.output, "layout.py")
            self.layout.user_data = self
            self.layout.dispatcher["layout_demand"] = layout_handle_layout_demand
            self.layout.dispatcher["namespace_in_use"] = layout_handle_namespace_in_use

def registry_handle_global(registry, id, interface, version):
    global layout_manager
    global output
    if interface == 'river_layout_manager_v3':
        layout_manager = registry.bind(id, RiverLayoutManagerV3, version)
    elif interface == 'wl_output':
        output = Output()
        output.output = registry.bind(id, WlOutput, version)
        output.id = id
        output.configure()
        outputs.append(output)

def registry_handle_global_remove(registry, id):
    for output in outputs:
        if output.id == id:
            output.destroy()
            outputs.remove(output)

display = Display()
display.connect()

registry = display.get_registry()
registry.dispatcher["global"] = registry_handle_global
registry.dispatcher["global_remove"] = registry_handle_global_remove

display.dispatch(block=True)
display.roundtrip()

if layout_manager == None:
    print("No layout_manager, aborting")
    quit()

for output in outputs:
    output.configure()

while loop and display.dispatch(block=True) != -1:
    pass

# Destroy outputs
for output in outputs:
    output.destroy()
    outputs.remove(output)

display.disconnect()