Merge branch 'master' of github.com:thepowersgang/acess2
[tpg/acess2.git] / Usermode / Applications / axwin3_src / WM / wm.c
1 /*
2  * Acess2 Window Manager v3
3  * - By John Hodge (thePowersGang)
4  *
5  * wm.c
6  * - Window manager core
7  */
8 #include <common.h>
9 #include <wm_renderer.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <video.h>
13 #include <wm_messages.h>
14 #include <decorator.h>
15
16 // === IMPORTS ===
17 extern void     IPC_SendWMMessage(tIPC_Client *Client, uint32_t Src, uint32_t Dst, int Msg, int Len, const void *Data);
18
19 // === GLOBALS ===
20 tWMRenderer     *gpWM_Renderers;
21 tWindow *gpWM_RootWindow;
22 //! Window which will recieve the next keyboard event
23 tWindow *gpWM_FocusedWindow;
24 //! Hilighted window (owner of the currently focused window)
25 tWindow *gpWM_HilightedWindow;
26
27 // === CODE ===
28 void WM_Initialise(void)
29 {
30         WM_CreateWindow(NULL, NULL, 0, 0x0088FF, "Background");
31         gpWM_RootWindow->W = giScreenWidth;
32         gpWM_RootWindow->H = giScreenHeight;
33         gpWM_RootWindow->Flags = WINFLAG_SHOW|WINFLAG_NODECORATE;
34 }
35
36 void WM_RegisterRenderer(tWMRenderer *Renderer)
37 {
38         // TODO: Catch out duplicates
39         Renderer->Next = gpWM_Renderers;
40         gpWM_Renderers = Renderer;
41 }
42
43 // --- Manipulation
44 tWindow *WM_CreateWindow(tWindow *Parent, tIPC_Client *Client, uint32_t ID, int RendererArg, const char *RendererName)
45 {
46         tWMRenderer     *renderer;
47         tWindow *ret;
48         
49         // - Get Renderer
50         for( renderer = gpWM_Renderers; renderer; renderer = renderer->Next )
51         {
52                 if(strcmp(RendererName, renderer->Name) == 0)
53                         break;
54         }
55         if(renderer == NULL)
56                 return NULL;
57
58         // - Call create window function
59         ret = renderer->CreateWindow(RendererArg);
60         ret->Client = Client;
61         ret->ID = ID;
62         ret->Parent = Parent;
63         if(!ret->Parent)
64                 ret->Parent = gpWM_RootWindow;
65         ret->Owner = Parent;
66         ret->Renderer = renderer;
67         ret->Flags |= WINFLAG_CLEAN;    // Needed to stop invaildate early exiting
68
69         // Append to parent
70         if(ret->Parent)
71         {
72                 if(ret->Parent->LastChild)
73                         ret->Parent->LastChild->NextSibling = ret;
74                 else
75                         ret->Parent->FirstChild = ret;
76                 ret->PrevSibling = ret->Parent->LastChild;
77                 ret->Parent->LastChild = ret;
78                 ret->NextSibling = NULL;
79         }
80         else
81         {
82                 gpWM_RootWindow = ret;
83         }
84
85         // Don't decorate child windows by default
86         if(Parent)
87         {
88                 ret->Flags |= WINFLAG_NODECORATE;
89         }
90         
91         // - Return!
92         return ret;
93 }
94
95 tWindow *WM_CreateWindowStruct(size_t ExtraSize)
96 {
97         tWindow *ret;
98         
99         ret = calloc( sizeof(tWindow) + ExtraSize, 1 );
100         ret->RendererInfo = ret + 1;    // Get end of tWindow
101         
102         return ret;
103 }
104
105 void WM_SetWindowTitle(tWindow *Window, const char *Title)
106 {
107         if(Window->Title)
108                 free(Window->Title);
109         Window->Title = strdup(Title);
110         _SysDebug("Window %p title set to '%s'", Window, Title);
111 }
112
113 void WM_RaiseWindow(tWindow *Window)
114 {
115         tWindow *parent = Window->Parent;
116         if(!Window->Parent)     return ;
117         
118         // Remove from list
119         if(Window->PrevSibling)
120                 Window->PrevSibling->NextSibling = Window->NextSibling;
121         if(Window->NextSibling)
122                 Window->NextSibling->PrevSibling = Window->PrevSibling;
123         if(parent->FirstChild == Window)
124                 parent->FirstChild = Window->NextSibling;
125         if(parent->LastChild == Window)
126                 parent->LastChild = Window->PrevSibling;
127
128         // Append to end
129         if(parent->LastChild)
130                 parent->LastChild->NextSibling = Window;
131         else
132                 parent->FirstChild = Window;
133         Window->PrevSibling = parent->LastChild;
134         Window->NextSibling = NULL;
135         parent->LastChild = Window;
136 }
137
138 void WM_FocusWindow(tWindow *Destination)
139 {
140         struct sWndMsg_Bool     _msg;
141         
142         if( gpWM_FocusedWindow == Destination )
143                 return ;
144         if( Destination && !(Destination->Flags & WINFLAG_SHOW) )
145                 return ;
146         
147         _msg.Val = 0;
148         WM_SendMessage(NULL, gpWM_FocusedWindow, WNDMSG_FOCUS, sizeof(_msg), &_msg);
149         _msg.Val = 1;
150         WM_SendMessage(NULL, Destination, WNDMSG_FOCUS, sizeof(_msg), &_msg);
151         
152         WM_Invalidate(gpWM_FocusedWindow);
153         WM_Invalidate(Destination);
154         gpWM_FocusedWindow = Destination;
155
156
157         // Get the owner of the focused window  
158 //      while(Destination && Destination->Owner)        Destination = Destination->Owner;
159 //      gpWM_HilightedWindow = Destination;
160 }
161
162
163 void WM_ShowWindow(tWindow *Window, int bShow)
164 {
165         struct sWndMsg_Bool     _msg;
166         
167         if( !!(Window->Flags & WINFLAG_SHOW) == bShow )
168                 return ;
169
170         // Message window
171         _msg.Val = !!bShow;
172         WM_SendMessage(NULL, Window, WNDMSG_SHOW, sizeof(_msg), &_msg);
173
174         // Update the flag
175         if(bShow)
176                 Window->Flags |= WINFLAG_SHOW;
177         else
178         {
179                 Window->Flags &= ~WINFLAG_SHOW;
180
181                 if( Window == gpWM_FocusedWindow )
182                         WM_FocusWindow(Window->Parent);
183                 
184                 // Just a little memory saving for large hidden windows
185                 if(Window->RenderBuffer) {
186                         free(Window->RenderBuffer);
187                         Window->RenderBuffer = NULL;
188                 }
189         }
190         
191         WM_Invalidate(Window);
192 }
193
194 void WM_DecorateWindow(tWindow *Window, int bDecorate)
195 {
196         if( !(Window->Flags & WINFLAG_NODECORATE) == !!bDecorate )
197                 return ;
198         
199         if(bDecorate)
200                 Window->Flags &= ~WINFLAG_NODECORATE;
201         else
202                 Window->Flags |= WINFLAG_NODECORATE;
203         
204         // Needed because the window size changes
205         if(Window->RenderBuffer) {
206                 free(Window->RenderBuffer);
207                 Window->RenderBuffer = NULL;
208         }
209         
210         WM_Invalidate(Window);
211 }
212
213 int WM_MoveWindow(tWindow *Window, int X, int Y)
214 {
215         // Clip coordinates
216         if(X + Window->W < 0)   X = -Window->W + 1;
217         if(Y + Window->H < 0)   Y = -Window->H + 1;
218         if(X >= giScreenWidth)  X = giScreenWidth - 1;
219         if(Y >= giScreenHeight) Y = giScreenHeight - 1;
220         
221         Window->X = X;  Window->Y = Y;
222
223         WM_Invalidate(Window);
224
225         return 0;
226 }
227
228 int WM_ResizeWindow(tWindow *Window, int W, int H)
229 {
230         if(W <= 0 || H <= 0 )   return 1;
231         if(Window->X + W < 0)   Window->X = -W + 1;
232         if(Window->Y + H < 0)   Window->Y = -H + 1;
233
234         Window->W = W;  Window->H = H;
235
236         if(Window->RenderBuffer) {
237                 free(Window->RenderBuffer);
238                 Window->RenderBuffer = NULL;
239         }
240         WM_Invalidate(Window);
241
242         {
243                 struct sWndMsg_Resize   msg;
244                 msg.W = W;
245                 msg.H = H;
246                 WM_SendMessage(NULL, Window, WNDMSG_RESIZE, sizeof(msg), &msg);
247         }
248         
249         return 0;
250 }
251
252 int WM_SendMessage(tWindow *Source, tWindow *Dest, int Message, int Length, const void *Data)
253 {
254         if(Dest == NULL)        return -2;
255         if(Length > 0 && Data == NULL)  return -1;
256
257         if( Decorator_HandleMessage(Dest, Message, Length, Data) != 1 )
258         {
259                 // TODO: Catch errors from ->HandleMessage
260                 return 0;
261         }
262         
263         // ->HandleMessage returns 1 when the message was not handled
264         if( Dest->Renderer->HandleMessage(Dest, Message, Length, Data) != 1 )
265         {
266                 // TODO: Catch errors from ->HandleMessage
267                 return 0;
268         }
269
270         // TODO: Implement message masking
271
272         if(Dest->Client)
273         {
274                 uint32_t        src_id;
275                 if(!Source)
276                         src_id = -1;
277                 else if(Source->Client != Dest->Client) {
278                         // TODO: Support different client source windows
279                         _SysDebug("WM_SendMessage: TODO - Support inter-client messages");
280                         return -1;
281                 }
282                 else {
283                         src_id = Source->ID;
284                 }
285                 
286                 IPC_SendWMMessage(Dest->Client, src_id, Dest->ID, Message, Length, Data);
287         }       
288
289         return 1;
290 }
291
292 void WM_Invalidate(tWindow *Window)
293 {
294         if(!Window)     return ;
295         _SysDebug("Invalidating %p", Window);
296         // Don't invalidate twice (speedup)
297 //      if( !(Window->Flags & WINFLAG_CLEAN) )  return;
298
299         // Mark for re-render
300         Window->Flags &= ~WINFLAG_CLEAN;
301
302         // Mark up the tree that a child window has changed     
303         while( (Window = Window->Parent) )
304                 Window->Flags &= ~WINFLAG_CHILDCLEAN;
305 }
306
307 // --- Rendering / Update
308 void WM_int_UpdateWindow(tWindow *Window)
309 {
310          int    bDecoratorRedraw = 0;
311
312         // Ignore hidden windows
313         if( !(Window->Flags & WINFLAG_SHOW) )
314                 return ;
315         
316         // Render
317         if( !(Window->Flags & WINFLAG_CLEAN) )
318         {
319                 // Calculate RealW/RealH
320                 if( !(Window->Flags & WINFLAG_NODECORATE) )
321                 {
322                         _SysDebug("Applying decorations to %p", Window);
323                         Decorator_UpdateBorderSize(Window);
324                         Window->RealW = Window->BorderL + Window->W + Window->BorderR;
325                         Window->RealH = Window->BorderT + Window->H + Window->BorderB;
326                         bDecoratorRedraw = 1;
327                 }
328                 else
329                 {
330                         Window->BorderL = 0;
331                         Window->BorderR = 0;
332                         Window->BorderT = 0;
333                         Window->BorderB = 0;
334                         Window->RealW = Window->W;
335                         Window->RealH = Window->H;
336                 }
337                 
338                 Window->Renderer->Redraw(Window);
339                 Window->Flags |= WINFLAG_CLEAN;
340         }
341         
342         // Process children
343         if( !(Window->Flags & WINFLAG_CHILDCLEAN) )
344         {
345                 tWindow *child;
346                 for( child = Window->FirstChild; child; child = child->NextSibling )
347                 {
348                         WM_int_UpdateWindow(child);
349                 }
350                 Window->Flags |= WINFLAG_CHILDCLEAN;
351         }
352         
353         if( bDecoratorRedraw )
354                 Decorator_Redraw(Window);
355 }
356
357 void WM_int_BlitWindow(tWindow *Window)
358 {
359         tWindow *child;
360
361         // Ignore hidden windows
362         if( !(Window->Flags & WINFLAG_SHOW) )
363                 return ;
364
365 //      _SysDebug("Blit %p to (%i,%i) %ix%i", Window, Window->X, Window->Y, Window->RealW, Window->RealH);
366         Video_Blit(Window->RenderBuffer, Window->X, Window->Y, Window->RealW, Window->RealH);
367         
368         if( Window == gpWM_FocusedWindow && Window->CursorW )
369         {
370                 Video_FillRect(
371                         Window->X + Window->BorderL + Window->CursorX,
372                         Window->Y + Window->BorderT + Window->CursorY,
373                         Window->CursorW, Window->CursorH,
374                         0x000000
375                         );
376                         
377         }
378
379         for( child = Window->FirstChild; child; child = child->NextSibling )
380         {
381                 WM_int_BlitWindow(child);
382         }
383 }
384
385 void WM_Update(void)
386 {
387         // Don't redraw if nothing has changed
388         if( (gpWM_RootWindow->Flags & WINFLAG_CHILDCLEAN) )
389                 return ;        
390
391         // - Iterate through visible windows, updating them as needed
392         WM_int_UpdateWindow(gpWM_RootWindow);
393         
394         // - Draw windows from back to front to the render buffer
395         WM_int_BlitWindow(gpWM_RootWindow);
396
397         Video_Update();
398 }
399

UCC git Repository :: git.ucc.asn.au