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

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