1 module shell;
2 
3 import compositor;
4 import output;
5 import wayland.server;
6 
7 import std.stdio;
8 import std.algorithm;
9 import std.range;
10 
11 class Shell : WlShell
12 {
13     Compositor comp;
14     ShellSurface[] topLevels;
15 
16     this (Compositor comp)
17     {
18         super(comp.display, ver);
19         this.comp = comp;
20     }
21 
22     void addTopLevel(ShellSurface ss)
23     {
24         topLevels ~= ss;
25         ss.addDestroyListener((WlResource res) {
26             topLevels = topLevels.remove!(tl => res is tl);
27         });
28     }
29 
30     void mouseButton(int x, int y, int button, WlPointer.ButtonState state)
31     {
32         foreach(tl; retro(topLevels))
33         {
34             if (x >= tl.x && x < tl.x+tl.width &&
35                 y >= tl.y && y < tl.y+tl.height)
36             {
37                 comp.seat.mouseButton(tl.client, button, state);
38                 break;
39             }
40         }
41     }
42 
43     // paint windows over background (last inserted on top)
44     // a more general algorithm should be needed for other kinds of surfaces (eg. cursors)
45     void paint(Output output)
46     {
47         immutable ow = output.width;
48         immutable oh = output.height;
49         auto ob = output.buf;
50         foreach (tl; topLevels)
51         {
52             if (!(tl.surf.outputMask & output.mask)) continue;
53 
54             auto sb = tl.surf.state.buffer;
55             auto sd = cast(uint[])sb.beginAccess();
56             scope(exit) sb.endAccess();
57 
58             if (tl.unplaced)
59             {
60                 tl.x = (ow - sb.width) / 2;
61                 tl.y = (oh - sb.height) / 2;
62                 tl.unplaced = false;
63             }
64 
65             tl.width = sb.width;
66             tl.height = sb.height;
67 
68             if (tl.x > ow) break;
69 
70             if (sb.format == WlShm.Format.xrgb8888)
71             {
72                 // no blending
73                 foreach (r; 0 .. sb.height)
74                 {
75                     if (r + tl.y > oh) break;
76 
77                     immutable srcFrom = r*sb.stride/4;
78                     immutable destFrom = (r+tl.y) * ow + tl.x;
79                     immutable copyWidth = min(
80                         sb.width, ow - tl.x
81                     );
82                     ob[destFrom .. destFrom+copyWidth] =
83                             sd[srcFrom .. srcFrom+copyWidth];
84                 }
85             }
86             else
87             {
88                 assert(sb.format == WlShm.Format.argb8888);
89                 // inefficient blending
90                 foreach (r; 0 .. sb.height)
91                 {
92                     if (r + tl.y > oh) break;
93 
94                     foreach (c; 0 .. sb.width)
95                     {
96                         if (c + tl.x > ow) break;
97 
98                         immutable size_t srcInd = r*sb.stride/4 + c;
99                         immutable size_t destInd = (r+tl.y) * ow + tl.x+c;
100 
101                         immutable uint dest = ob[destInd];
102                         immutable uint aDest = (dest & 0xff000000) >>> 24;
103                         immutable uint rDest = (dest & 0xff0000) >>> 16;
104                         immutable uint gDest = (dest & 0xff00) >>> 8;
105                         immutable uint bDest = (dest & 0xff);
106 
107                         immutable uint src = sd[srcInd];
108                         immutable uint aSrc = (src & 0xff000000) >>> 24;
109                         immutable uint rSrc = (src & 0xff0000) >>> 16;
110                         immutable uint gSrc = (src & 0xff00) >>> 8;
111                         immutable uint bSrc = (src & 0xff);
112 
113                         auto aRes = aSrc + aDest*(255 - aSrc) / 255;
114 
115                         auto rRes = ((aSrc*rSrc) + (255-aSrc)*(aDest*rDest)/255) / aRes;
116                         auto gRes = ((aSrc*gSrc) + (255-aSrc)*(aDest*gDest)/255) / aRes;
117                         auto bRes = ((aSrc*bSrc) + (255-aSrc)*(aDest*bDest)/255) / aRes;
118 
119                         if (aRes > 0xff) aRes = 0xff;
120                         if (rRes > 0xff) rRes = 0xff;
121                         if (gRes > 0xff) gRes = 0xff;
122                         if (bRes > 0xff) bRes = 0xff;
123 
124                         ob[destInd] = aRes << 24 | rRes << 16 | gRes << 8 | bRes;
125                     }
126                 }
127             }
128         }
129     }
130 
131     // WlShell
132 
133     override WlShellSurface getShellSurface(WlClient cl, Resource res, uint id, WlSurface surf)
134     {
135         return new ShellSurface(cl, id, cast(Surface)surf, res, comp);
136     }
137 }
138 
139 
140 class ShellSurface : WlShellSurface
141 {
142     Surface surf;
143     WlShell.Resource shRes;
144     Compositor comp;
145     bool unplaced = true;
146     int x; int y;
147     int width; int height;
148 
149     this(WlClient cl, uint id, Surface surf, WlShell.Resource shRes, Compositor comp)
150     {
151         super(cl, ver, id);
152         this.surf = surf;
153         this.shRes = shRes;
154         this.comp = comp;
155     }
156 
157     @property Shell shell()
158     {
159         return cast(Shell)shRes.outer;
160     }
161 
162     // WlShellSurface
163 
164     override protected void pong(WlClient cl,
165                                  uint serial)
166     {}
167 
168     override protected void move(WlClient cl,
169                                  WlSeat.Resource seat,
170                                  uint serial)
171     {}
172 
173     override protected void resize(WlClient cl,
174                                    WlSeat.Resource seat,
175                                    uint serial,
176                                    Resize edges)
177     {}
178 
179 
180     override protected void setToplevel(WlClient cl)
181     {
182         try {
183             surf.assignRole("shell");
184             auto output = comp.outputs[0];
185             surf.outputMask = surf.outputMask | output.mask;
186             shell.addTopLevel(this);
187         }
188         catch (Exception ex)
189         {
190             shRes.postError(WlShell.Error.role, ex.msg);
191         }
192     }
193 
194     override protected void setTransient(WlClient cl,
195                                          WlSurface parent,
196                                          int x,
197                                          int y,
198                                          Transient flags)
199     {}
200 
201     override protected void setFullscreen(WlClient cl,
202                                           FullscreenMethod method,
203                                           uint framerate,
204                                           WlOutput.Resource outputRes)
205     {}
206 
207     override protected void setPopup(WlClient cl,
208                                      WlSeat.Resource seat,
209                                      uint serial,
210                                      WlSurface parent,
211                                      int x,
212                                      int y,
213                                      Transient flags)
214     {}
215 
216     override protected void setMaximized(WlClient cl,
217                                          WlOutput.Resource output)
218     {}
219 
220     override protected void setTitle(WlClient cl,
221                                      string title)
222     {
223 
224     }
225 
226     override protected void setClass(WlClient cl,
227                                      string class_)
228     {}
229 
230 }