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

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