Usermode/AxWin3 - Compile fix
[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 extern void     IPC_SendReply(tIPC_Client *Client, uint32_t WinID, int MsgID, size_t Len, const void *Data);
19 extern tWindow  *IPC_int_GetWindow(tIPC_Client *Client, uint32_t ID);
20
21 // === GLOBALS ===
22 tWMRenderer     *gpWM_Renderers;
23 tWindow *gpWM_RootWindow;
24 //! Window which will recieve the next keyboard event
25 tWindow *gpWM_FocusedWindow;
26 //! Hilighted window (owner of the currently focused window)
27 tWindow *gpWM_HilightedWindow;
28
29 // === CODE ===
30 void WM_Initialise(void)
31 {
32         WM_CreateWindow(NULL, NULL, 0, 0x0088FF, "Background");
33         gpWM_RootWindow->W = giScreenWidth;
34         gpWM_RootWindow->H = giScreenHeight;
35         gpWM_RootWindow->Flags = WINFLAG_SHOW|WINFLAG_NODECORATE;
36 }
37
38 void WM_RegisterRenderer(tWMRenderer *Renderer)
39 {
40         // TODO: Catch out duplicates
41         Renderer->Next = gpWM_Renderers;
42         gpWM_Renderers = Renderer;
43 }
44
45 // --- Manipulation
46 tWindow *WM_CreateWindow(tWindow *Parent, tIPC_Client *Client, uint32_t ID, int RendererArg, const char *RendererName)
47 {
48         tWMRenderer     *renderer;
49         tWindow *ret;
50         
51         // - Get Renderer
52         for( renderer = gpWM_Renderers; renderer; renderer = renderer->Next )
53         {
54                 if(strcmp(RendererName, renderer->Name) == 0)
55                         break;
56         }
57         if(renderer == NULL)
58                 return NULL;
59
60         // - Call create window function
61         ret = renderer->CreateWindow(RendererArg);
62         ret->Client = Client;
63         ret->ID = ID;
64         ret->Parent = Parent;
65         if(!ret->Parent)
66                 ret->Parent = gpWM_RootWindow;
67         ret->Owner = Parent;
68         ret->Renderer = renderer;
69         ret->Flags |= WINFLAG_CLEAN;    // Needed to stop invaildate early exiting
70
71         // Append to parent
72         if(ret->Parent)
73         {
74                 if(ret->Parent->LastChild)
75                         ret->Parent->LastChild->NextSibling = ret;
76                 else
77                         ret->Parent->FirstChild = ret;
78                 ret->PrevSibling = ret->Parent->LastChild;
79                 ret->Parent->LastChild = ret;
80                 ret->NextSibling = NULL;
81         }
82         else
83         {
84                 gpWM_RootWindow = ret;
85         }
86
87         // Don't decorate child windows by default
88         if(Parent)
89         {
90                 ret->Flags |= WINFLAG_NODECORATE;
91         }
92         
93         // - Return!
94         return ret;
95 }
96
97 void WM_DestroyWindow(tWindow *Window)
98 {
99         // TODO: Lock window and flag as invalid
100         
101         // - Remove from render tree
102         {
103                 // TODO: Lock render tree?
104                 tWindow *prev = Window->PrevSibling;
105                 tWindow *next = Window->NextSibling;
106                 if(prev)
107                         prev->NextSibling = next;
108                 else
109                         Window->Parent->FirstChild = next;
110                 if(next)
111                         next->PrevSibling = prev;
112                 else
113                         Window->Parent->LastChild = prev;
114         }
115         WM_Invalidate(Window->Parent);
116         
117         // - Remove from inheritance tree?
118         
119         // - Clean up render children
120         // Lock should not be needed
121         tWindow *win, *next;
122         for( win = Window->FirstChild; win; win = next )
123         {
124                 next = win->NextSibling;
125                 ASSERT(Window->FirstChild->Parent == Window);
126                 WM_DestroyWindow(win);
127         }
128         
129         // - Clean up inheriting children?
130
131         // - Tell renderer to clean up
132         if( Window->Renderer->DestroyWindow )
133                 Window->Renderer->DestroyWindow(Window);
134         else
135                 _SysDebug("WARN: Renderer %s does not have a destroy function", Window->Renderer->Name);
136         
137         // - Clean up render cache and window structure
138         free(Window->Title);
139         free(Window->RenderBuffer);
140         free(Window);
141 }
142
143 tWindow *WM_GetWindowByID(tWindow *Requester, uint32_t ID)
144 {
145         return IPC_int_GetWindow(Requester->Client, ID);
146 }
147
148 tWindow *WM_CreateWindowStruct(size_t ExtraSize)
149 {
150         tWindow *ret;
151         
152         ret = calloc( sizeof(tWindow) + ExtraSize, 1 );
153         ret->RendererInfo = ret + 1;    // Get end of tWindow
154         
155         return ret;
156 }
157
158 void WM_SetWindowTitle(tWindow *Window, const char *Title)
159 {
160         if(Window->Title)
161                 free(Window->Title);
162         Window->Title = strdup(Title);
163         _SysDebug("Window %p title set to '%s'", Window, Title);
164 }
165
166 void WM_RaiseWindow(tWindow *Window)
167 {
168         tWindow *parent = Window->Parent;
169         if(!Window->Parent)     return ;
170         
171         // Remove from list
172         if(Window->PrevSibling)
173                 Window->PrevSibling->NextSibling = Window->NextSibling;
174         if(Window->NextSibling)
175                 Window->NextSibling->PrevSibling = Window->PrevSibling;
176         if(parent->FirstChild == Window)
177                 parent->FirstChild = Window->NextSibling;
178         if(parent->LastChild == Window)
179                 parent->LastChild = Window->PrevSibling;
180
181         // Append to end
182         if(parent->LastChild)
183                 parent->LastChild->NextSibling = Window;
184         else
185                 parent->FirstChild = Window;
186         Window->PrevSibling = parent->LastChild;
187         Window->NextSibling = NULL;
188         parent->LastChild = Window;
189         
190         _SysDebug("Raised %p", Window);
191 }
192
193 /*
194 void WM_RaiseWindow(tWindow *Window)
195 {
196         // Move to the last render position (move to top)
197         while(Window && Window->Parent)
198         {
199                 if( Window->NextSibling )
200                 {
201                         // remove
202                         if( Window->PrevSibling )
203                                 Window->PrevSibling->NextSibling = Window->NextSibling;
204                         Window->NextSibling->PrevSibling = Window->PrevSibling;
205                         // Mutate self
206                         Window->PrevSibling = Window->Parent->LastChild;
207                         Window->NextSibling = NULL;
208                         // re-add
209                         Window->PrevSibling->NextSibling = Window;
210                         Window->Parent->LastChild = Window;
211                 }
212                 _SysDebug("WM_RaiseWindow: Raised %p", Window);
213                 Window = Window->Parent;
214         }
215 }
216 */
217
218 void WM_FocusWindow(tWindow *Destination)
219 {
220         struct sWndMsg_Bool     _msg;
221         
222         _SysDebug("WM_FocusWindow(%p)", Destination);
223
224         if( gpWM_FocusedWindow == Destination )
225                 return ;
226         if( Destination && !(Destination->Flags & WINFLAG_SHOW) )
227                 return ;
228         
229         if( gpWM_FocusedWindow )
230         {
231                 _msg.Val = 0;
232                 WM_SendMessage(NULL, gpWM_FocusedWindow, WNDMSG_FOCUS, sizeof(_msg), &_msg);
233         }
234         if( Destination )
235         {
236                 _msg.Val = 1;
237                 WM_SendMessage(NULL, Destination, WNDMSG_FOCUS, sizeof(_msg), &_msg);
238         }
239                 
240         WM_Invalidate(gpWM_FocusedWindow);
241         WM_Invalidate(Destination);
242
243         gpWM_FocusedWindow = Destination;
244 }
245
246
247 void WM_ShowWindow(tWindow *Window, int bShow)
248 {
249         struct sWndMsg_Bool     _msg;
250         
251         if( !!(Window->Flags & WINFLAG_SHOW) == bShow )
252                 return ;
253
254         // Message window
255         _msg.Val = !!bShow;
256         WM_SendMessage(NULL, Window, WNDMSG_SHOW, sizeof(_msg), &_msg);
257
258         // Update the flag
259         if(bShow) {
260                 Window->Flags |= WINFLAG_SHOW;
261                 _SysDebug("Window %p shown", Window);
262         }
263         else
264         {
265                 Window->Flags &= ~WINFLAG_SHOW;
266
267                 if( Window == gpWM_FocusedWindow )
268                         WM_FocusWindow(Window->Parent);
269                 
270                 // Just a little memory saving for large hidden windows
271                 if(Window->RenderBuffer) {
272                         free(Window->RenderBuffer);
273                         Window->RenderBuffer = NULL;
274                 }
275                 _SysDebug("Window %p hidden", Window);
276         }
277         
278         WM_Invalidate(Window);
279 }
280
281 void WM_DecorateWindow(tWindow *Window, int bDecorate)
282 {
283         if( !(Window->Flags & WINFLAG_NODECORATE) == !!bDecorate )
284                 return ;
285         
286         if(bDecorate)
287                 Window->Flags &= ~WINFLAG_NODECORATE;
288         else
289                 Window->Flags |= WINFLAG_NODECORATE;
290         
291         // Needed because the window size changes
292         if(Window->RenderBuffer) {
293                 free(Window->RenderBuffer);
294                 Window->RenderBuffer = NULL;
295         }
296         
297         WM_Invalidate(Window);
298 }
299
300 void WM_SetRelative(tWindow *Window, int bRelativeToParent)
301 {
302 //      _SysDebug("WM_SetRelative: (%p{Parent=%p},%i)", Window, Window->Parent, bRelativeToParent);
303         // No meaning if no parent
304         if( !Window->Parent )
305                 return ;
306
307         // Check that the flag is changing
308         if( !!bRelativeToParent == !!(Window->Flags & WINFLAG_RELATIVE) )
309                 return ;
310
311         if( bRelativeToParent ) {
312                 // Set
313                 Window->Flags |= WINFLAG_RELATIVE;
314                 WM_MoveWindow(Window, Window->X, Window->Y);
315         }
316         else {
317                 // Clear
318                 Window->Flags &= ~WINFLAG_RELATIVE;
319                 WM_MoveWindow(Window, Window->X - Window->Parent->X, Window->Y - Window->Parent->Y);
320         }
321 }
322
323 int WM_MoveWindow(tWindow *Window, int X, int Y)
324 {
325 //      _SysDebug("Move %p to (%i,%i)", Window, X, Y);
326         // Clip coordinates
327         if(X + Window->W < 0)   X = -Window->W + 1;
328         if(Y + Window->H < 0)   Y = -Window->H + 1;
329         if(X >= giScreenWidth)  X = giScreenWidth - 1;
330         if(Y >= giScreenHeight) Y = giScreenHeight - 1;
331         
332         // If relative to the parent, extra checks
333         if( (Window->Flags & WINFLAG_RELATIVE) && Window->Parent )
334         {
335                 if( X > Window->Parent->W )     return 1;
336                 if( Y > Window->Parent->H )     return 1;
337         }
338         // TODO: Re-sanitise
339
340         _SysDebug("WM_MoveWindow: (%i,%i)", X, Y);
341         Window->X = X;  Window->Y = Y;
342
343         // Mark up the tree that a child window has changed     
344         while( (Window = Window->Parent) )
345                 Window->Flags &= ~WINFLAG_CHILDCLEAN;
346
347         return 0;
348 }
349
350 int WM_ResizeWindow(tWindow *Window, int W, int H)
351 {
352         if(W <= 0 || H <= 0 )   return 1;
353         if(Window->X + W < 0)   Window->X = -W + 1;
354         if(Window->Y + H < 0)   Window->Y = -H + 1;
355
356         if( Window->W == W && Window->H == H )
357                 return 0;
358
359         _SysDebug("WM_Resizeindow: %ix%i", W, H);
360         Window->W = W;  Window->H = H;
361
362         if(Window->RenderBuffer) {
363                 free(Window->RenderBuffer);
364                 Window->RenderBuffer = NULL;
365         }
366         WM_Invalidate(Window);
367
368         {
369                 struct sWndMsg_Resize   msg;
370                 msg.W = W;
371                 msg.H = H;
372                 WM_SendMessage(NULL, Window, WNDMSG_RESIZE, sizeof(msg), &msg);
373         }
374         
375         return 0;
376 }
377
378 int WM_SendMessage(tWindow *Source, tWindow *Dest, int Message, int Length, const void *Data)
379 {
380 //      _SysDebug("WM_SendMessage: (%p, %p, %i, %i, %p)", Source, Dest, Message, Length, Data);
381         if(Dest == NULL) {
382                 _SysDebug("WM_SendMessage: NULL destination from %p", __builtin_return_address(0));
383                 return -2;
384         }
385         if(Length > 0 && Data == NULL) {
386                 _SysDebug("WM_SendMessage: non-zero length and NULL data");
387                 return -1;
388         }
389
390         if( Decorator_HandleMessage(Dest, Message, Length, Data) != 1 )
391         {
392                 // TODO: Catch errors from ->HandleMessage
393 //              _SysDebug("WM_SendMessage: Decorator grabbed message?");
394                 return 0;
395         }
396         
397         // ->HandleMessage returns 1 when the message was not handled
398         if( Dest->Renderer->HandleMessage(Dest, Message, Length, Data) != 1 )
399         {
400                 // TODO: Catch errors from ->HandleMessage
401 //              _SysDebug("WM_SendMessage: Renderer grabbed message?");
402                 return 0;
403         }
404
405         // TODO: Implement message masking
406
407         // Dispatch to client
408         if(Dest->Client)
409         {
410                 uint32_t        src_id;
411                 if(!Source)
412                         src_id = -1;
413                 else if(Source->Client != Dest->Client) {
414                         // TODO: Support different client source windows
415                         _SysDebug("WM_SendMessage: TODO - Support inter-client messages");
416                         return -1;
417                 }
418                 else {
419                         src_id = Source->ID;
420                 }
421                 
422 //              _SysDebug("WM_SendMessage: IPC Dispatch");
423                 IPC_SendWMMessage(Dest->Client, src_id, Dest->ID, Message, Length, Data);
424         }       
425
426         return 1;
427 }
428
429 int WM_SendIPCReply(tWindow *Window, int Message, size_t Length, const void *Data)
430 {
431         IPC_SendReply(Window->Client, Window->ID, Message, Length, Data);
432         return 0;
433 }
434
435 void WM_Invalidate(tWindow *Window)
436 {
437         if(!Window)     return ;
438 //      _SysDebug("Invalidating %p", Window);
439         // Don't invalidate twice (speedup)
440 //      if( !(Window->Flags & WINFLAG_CLEAN) )  return;
441
442         // Mark for re-render
443         Window->Flags &= ~WINFLAG_CLEAN;
444
445         // Mark up the tree that a child window has changed     
446         while( (Window = Window->Parent) )
447                 Window->Flags &= ~WINFLAG_CHILDCLEAN;
448 }
449
450 // --- Rendering / Update
451 void WM_int_UpdateWindow(tWindow *Window)
452 {
453          int    bDecoratorRedraw = 0;
454
455         // Ignore hidden windows
456         if( !(Window->Flags & WINFLAG_SHOW) )
457                 return ;
458         
459         if( (Window->Flags & WINFLAG_RELATIVE) && Window->Parent )
460         {
461                 Window->RealX = Window->Parent->RealX + Window->Parent->BorderL + Window->X;
462                 Window->RealY = Window->Parent->RealY + Window->Parent->BorderT + Window->Y;
463         }
464         else
465         {
466                 Window->RealX = Window->X;
467                 Window->RealY = Window->Y;
468         }
469         
470         // Render
471         if( !(Window->Flags & WINFLAG_CLEAN) )
472         {
473                 // Calculate RealW/RealH
474                 if( !(Window->Flags & WINFLAG_NODECORATE) )
475                 {
476                         //_SysDebug("Applying decorations to %p", Window);
477                         Decorator_UpdateBorderSize(Window);
478                         Window->RealW = Window->BorderL + Window->W + Window->BorderR;
479                         Window->RealH = Window->BorderT + Window->H + Window->BorderB;
480                         bDecoratorRedraw = 1;
481                 }
482                 else
483                 {
484                         Window->BorderL = 0;
485                         Window->BorderR = 0;
486                         Window->BorderT = 0;
487                         Window->BorderB = 0;
488                         Window->RealW = Window->W;
489                         Window->RealH = Window->H;
490                 }
491
492                 Window->Renderer->Redraw(Window);
493                 Window->Flags |= WINFLAG_CLEAN;
494         }
495         
496         // Process children
497         if( !(Window->Flags & WINFLAG_CHILDCLEAN) )
498         {
499                 tWindow *child;
500                 for( child = Window->FirstChild; child; child = child->NextSibling )
501                 {
502                         WM_int_UpdateWindow(child);
503                 }
504                 Window->Flags |= WINFLAG_CHILDCLEAN;
505         }
506         
507         if( bDecoratorRedraw )
508                 Decorator_Redraw(Window);
509 }
510
511 void WM_int_BlitWindow(tWindow *Window)
512 {
513         tWindow *child;
514
515         // Ignore hidden windows
516         if( !(Window->Flags & WINFLAG_SHOW) )
517                 return ;
518         
519         // Duplicated position update to handle window moving
520         if( (Window->Flags & WINFLAG_RELATIVE) && Window->Parent )
521         {
522                 Window->RealX = Window->Parent->RealX + Window->Parent->BorderL + Window->X;
523                 Window->RealY = Window->Parent->RealY + Window->Parent->BorderT + Window->Y;
524         }
525         else
526         {
527                 Window->RealX = Window->X;
528                 Window->RealY = Window->Y;
529         }
530
531 //      _SysDebug("Blit %p (%p) to (%i,%i) %ix%i", Window, Window->RenderBuffer,
532 //              Window->RealX, Window->RealY, Window->RealW, Window->RealH);
533         Video_Blit(Window->RenderBuffer, Window->RealX, Window->RealY, Window->RealW, Window->RealH);
534         
535         if( Window == gpWM_FocusedWindow && Window->CursorW )
536         {
537                 Video_FillRect(
538                         Window->RealX + Window->BorderL + Window->CursorX,
539                         Window->RealY + Window->BorderT + Window->CursorY,
540                         Window->CursorW, Window->CursorH,
541                         0x000000
542                         );
543         }
544
545         for( child = Window->FirstChild; child; child = child->NextSibling )
546         {
547                 WM_int_BlitWindow(child);
548         }
549 }
550
551 void WM_Update(void)
552 {
553         // Don't redraw if nothing has changed
554         if( (gpWM_RootWindow->Flags & WINFLAG_CHILDCLEAN) )
555                 return ;        
556
557         // - Iterate through visible windows, updating them as needed
558         WM_int_UpdateWindow(gpWM_RootWindow);
559         
560         // - Draw windows from back to front to the render buffer
561         WM_int_BlitWindow(gpWM_RootWindow);
562
563         Video_Update();
564 }
565

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