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

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