Merge branch 'master' of git://github.com/thepowersgang/acess2
[tpg/acess2.git] / Usermode / Applications / axwin4_src / Server / ipc.cpp
1 /*
2  * Acess2 GUI v4
3  * - By John Hodge (thePowersGang)
4  *
5  * ipc.cpp
6  * - Client-Server communication (dispatch)
7  */
8 #define __STDC_LIMIT_MACROS
9 #include <ipc.hpp>
10 #include <list>
11 #include <IIPCChannel.hpp>
12 #include <algorithm>
13 #include <CClient.hpp>
14 #include <serialisation.hpp>
15 #include <ipc_proto.hpp>
16 #include <CCompositor.hpp>
17 extern "C" {
18 #include <assert.h>
19 };
20 #include <CIPCChannel_AcessIPCPipe.hpp>
21 #include <draw_control.hpp>
22 #include <draw_text.hpp>
23
24 namespace AxWin {
25 namespace IPC {
26
27 CCompositor*    gpCompositor;
28 ::std::list<IIPCChannel*>       glChannels;
29 ::std::map<uint16_t,CClient*>   glClients;
30 uint16_t        giNextClient = 1;
31
32 void Initialise(const CConfigIPC& config, CCompositor& compositor)
33 {
34         gpCompositor = &compositor;
35         
36         ::std::string pipe_basepath = "axwin4";
37         glChannels.push_back( new CIPCChannel_AcessIPCPipe( pipe_basepath ) );
38
39         //glChannels.push_back( new CIPCChannel_TCP("0.0.0.0:2100") );
40         
41         //for( auto channel : config.m_channels )
42         //{
43         //      channels.push_back(  );
44         //}
45 }
46
47 int FillSelect(fd_set& rfds)
48 {
49         int ret = 0;
50         for( const auto channel : glChannels )
51         {
52                 assert(channel);
53                 ret = ::std::max(ret, channel->FillSelect(rfds));
54         }
55         return ret;
56 }
57
58 void HandleSelect(const fd_set& rfds)
59 {
60         for( const auto channel : glChannels )
61         {
62                 assert(channel);
63                 channel->HandleSelect(rfds);
64         }
65 }
66
67 void RegisterClient(CClient& client)
68 {
69         _SysDebug("RegisterClient(&client=%p)", &client);
70         // allocate a client ID, and save
71         for( int i = 0; i < 100; i ++ )
72         {
73                 uint16_t id = giNextClient++;
74                 if(giNextClient == 0)   giNextClient = 1;
75                 auto r = glClients.insert( ::std::pair<uint16_t,CClient*>(id, &client) );
76                 if( r.second == true )
77                 {
78                         client.set_id(id);
79                         return;
80                 }
81         }
82         // Wut? 100 attempts and fail!
83         assert(!"Todo - Better way of handling client ID reuse");
84 }
85
86 CClient* GetClientByID(uint16_t id)
87 {
88         auto it = glClients.find(id);
89         if(it == glClients.end()) {
90                 //_SysDebug("Client %i not registered", id);
91                 return nullptr;
92         }
93         else {
94                 //_SysDebug("Client %i %i = %p", id, it->first, it->second);
95                 return it->second;
96         }
97 }
98
99 void DeregisterClient(CClient& client)
100 {
101         glClients.erase( client.id() );
102 }
103
104
105 void SendMessage_NotifyDims(CClient& client, unsigned int WinID, unsigned int NewW, unsigned int NewH)
106 {
107         _SysDebug("TODO: IPC::SendMessage_NotifyDims");
108 }
109 void SendMessage_MouseButton(CClient& client, unsigned int WinID, unsigned int X, unsigned int Y, uint8_t Button, bool Pressed)
110 {
111         CSerialiser     msg;
112         msg.WriteU8(IPCMSG_INPUTEVENT);
113         msg.WriteU8(IPC_INEV_MOUSEBTN);
114         msg.WriteU16(WinID);
115         msg.WriteU16(X);
116         msg.WriteU16(Y);
117         msg.WriteU8(Button);
118         msg.WriteU8(Pressed ? 0 : 1);
119         client.SendMessage(msg);
120 }
121 void SendMessage_MouseMove(CClient& client, unsigned int WinID, unsigned int X, unsigned int Y)
122 {
123         _SysDebug("TODO: IPC::SendMessage_MouseMove");
124 }
125 void SendMessage_KeyEvent(CClient& client, unsigned int WinID, uint32_t KeySym, bool Pressed, const char *Translated)
126 {
127         CSerialiser     msg;
128         msg.WriteU8(IPCMSG_INPUTEVENT);
129         msg.WriteU8(IPC_INEV_KEYBOARD);
130         msg.WriteU16(WinID);
131         msg.WriteU16(KeySym);
132         msg.WriteU8(Pressed ? 0 : 1);
133         msg.WriteString(Translated);
134         client.SendMessage(msg);
135 }
136
137
138 void HandleMessage_Nop(CClient& client, CDeserialiser& message)
139 {
140         // Do nothing
141 }
142 void HandleMessage_Reply(CClient& client, CDeserialiser& message)
143 {
144         // Reply to a sent message
145         // - Not many messages need server-bound replies
146         int orig_command = message.ReadU8();
147         switch(orig_command)
148         {
149         case IPCMSG_PING:
150                 // Ping reply, mark client as still responding
151                 break;
152         default:
153                 // Unexpected reply
154                 break;
155         }
156 }
157
158 void HandleMessage_Ping(CClient& client, CDeserialiser& message)
159 {
160         // A client has asked for a ping, we pong them back
161         CSerialiser     reply;
162         reply.WriteU8(IPCMSG_REPLY);
163         reply.WriteU8(IPCMSG_PING);
164         client.SendMessage(reply);
165 }
166
167 void HandleMessage_GetGlobalAttr(CClient& client, CDeserialiser& message)
168 {
169         uint16_t        attr_id = message.ReadU16();
170         
171         CSerialiser     reply;
172         reply.WriteU8(IPCMSG_REPLY);
173         reply.WriteU8(IPCMSG_GETGLOBAL);
174         reply.WriteU16(attr_id);
175         
176         switch(attr_id)
177         {
178         case IPC_GLOBATTR_SCREENDIMS: {
179                 uint8_t screen_id = message.ReadU8();
180                 unsigned int w, h;
181                 gpCompositor->GetScreenDims(screen_id, &w, &h);
182                 reply.WriteU16( (w <= UINT16_MAX ? w : UINT16_MAX) );
183                 reply.WriteU16( (h <= UINT16_MAX ? h : UINT16_MAX) );
184                 break; }
185         case IPC_GLOBATTR_MAXAREA:
186                 assert(!"TODO: IPC_GLOBATTR_MAXAREA");
187                 break;
188         default:
189                 throw IPC::CClientFailure("Bad global attribute ID");
190         }
191         
192         client.SendMessage(reply);
193 }
194
195 void HandleMessage_SetGlobalAttr(CClient& client, CDeserialiser& message)
196 {
197         uint16_t        attr_id = message.ReadU16();
198         
199         switch(attr_id)
200         {
201         case IPC_GLOBATTR_SCREENDIMS:
202                 // Setting readonly
203                 break;
204         case IPC_GLOBATTR_MAXAREA:
205                 assert(!"TODO: IPC_GLOBATTR_MAXAREA");
206                 break;
207         default:
208                 throw IPC::CClientFailure("Bad global attribute ID");
209         }
210 }
211
212 void HandleMessage_CreateWindow(CClient& client, CDeserialiser& message)
213 {
214         uint16_t        new_id = message.ReadU16();
215         //uint16_t      parent_id = message.ReadU16();
216         //CWindow* parent = client.GetWindow( parent_id );
217         ::std::string   name = message.ReadString();
218         
219         ::_SysDebug("_CreateWindow: (%i, '%s')", new_id, name.c_str());
220         client.SetWindow( new_id, new CWindow(*gpCompositor, client, name, new_id) );
221 }
222
223 void HandleMessage_DestroyWindow(CClient& client, CDeserialiser& message)
224 {
225         uint16_t        win_id = message.ReadU16();
226         _SysDebug("_DestroyWindow: (%i)", win_id);
227         
228         CWindow*        win = client.GetWindow(win_id);
229         if(!win) {
230                 throw IPC::CClientFailure("_DestroyWindow: Bad window");
231         }
232         client.SetWindow(win_id, 0);    
233         
234         // TODO: Directly inform compositor?
235         delete win;
236 }
237
238 void HandleMessage_SetWindowAttr(CClient& client, CDeserialiser& message)
239 {
240         uint16_t        win_id = message.ReadU16();
241         uint16_t        attr_id = message.ReadU16();
242         _SysDebug("_SetWindowAttr: (Win=%i, ID=%i)", win_id, attr_id);
243         
244         CWindow*        win = client.GetWindow(win_id);
245         if(!win) {
246                 throw IPC::CClientFailure("_SetWindowAttr - Bad window");
247         }
248         
249         switch(attr_id)
250         {
251         case IPC_WINATTR_DIMENSIONS: {
252                 uint16_t new_w = message.ReadU16();
253                 uint16_t new_h = message.ReadU16();
254                 win->Resize(new_w, new_h);
255                 break; }
256         case IPC_WINATTR_POSITION: {
257                 int16_t new_x = message.ReadS16();
258                 int16_t new_y = message.ReadS16();
259                 win->Move(new_x, new_y);
260                 break; }
261         case IPC_WINATTR_SHOW:
262                 win->Show( message.ReadU8() != 0 );
263                 break;
264         case IPC_WINATTR_FLAGS:
265                 win->SetFlags( message.ReadU8() );      // TODO: U8? why so small?
266                 break;
267         case IPC_WINATTR_TITLE:
268                 assert(!"TODO: IPC_WINATTR_TITLE");
269                 break;
270         default:
271                 _SysDebug("HandleMessage_SetWindowAttr - Bad attr %u", attr_id);
272                 throw IPC::CClientFailure("Bad window attr");
273         }
274 }
275
276 void HandleMessage_GetWindowAttr(CClient& client, CDeserialiser& message)
277 {
278         assert(!"TODO HandleMessage_GetWindowAttr");
279 }
280
281 void HandleMessage_SendIPC(CClient& client, CDeserialiser& message)
282 {
283         assert(!"TODO HandleMessage_SendIPC");
284 }
285
286 void HandleMessage_GetWindowBuffer(CClient& client, CDeserialiser& message)
287 {
288         uint16_t        win_id = message.ReadU16();
289         _SysDebug("_GetWindowBuffer: (%i)", win_id);
290         
291         CWindow*        win = client.GetWindow(win_id);
292         if(!win) {
293                 throw IPC::CClientFailure("_PushData: Bad window");
294         }
295         
296         uint64_t handle = win->m_surface.GetSHMHandle();
297         
298         CSerialiser     reply;
299         reply.WriteU8(IPCMSG_REPLY);
300         reply.WriteU8(IPCMSG_GETWINBUF);
301         reply.WriteU16(win_id);
302         reply.WriteU64(handle);
303         client.SendMessage(reply);
304 }
305
306 void HandleMessage_DamageRect(CClient& client, CDeserialiser& message)
307 {
308         uint16_t        winid = message.ReadU16();
309         uint16_t        x = message.ReadU16();
310         uint16_t        y = message.ReadU16();
311         uint16_t        w = message.ReadU16();
312         uint16_t        h = message.ReadU16();
313         
314         _SysDebug("_DamageRect: (%i %i,%i %ix%i)", winid, x, y, w, h);
315         
316         CWindow*        win = client.GetWindow(winid);
317         if(!win) {
318                 throw IPC::CClientFailure("_PushData: Bad window");
319         }
320         
321         CRect   area(x,y,w,h);
322         
323         win->Repaint(area);
324 }
325
326 void HandleMessage_PushData(CClient& client, CDeserialiser& message)
327 {
328         uint16_t        win_id = message.ReadU16();
329         uint16_t        x = message.ReadU16();
330         uint16_t        y = message.ReadU16();
331         uint16_t        w = message.ReadU16();
332         uint16_t        h = message.ReadU16();
333         _SysDebug("_PushData: (%i, (%i,%i) %ix%i)", win_id, x, y, w, h);
334         
335         CWindow*        win = client.GetWindow(win_id);
336         if(!win) {
337                 throw IPC::CClientFailure("_PushData: Bad window");
338         }
339         
340         for( unsigned int row = 0; row < h; row ++ )
341         {
342                 const ::std::vector<uint8_t> scanline_data = message.ReadBuffer();
343                 if( scanline_data.size() != w * 4 ) {
344                         _SysDebug("ERROR _PushData: Scanline buffer size mismatch (%i,%i)",
345                                 scanline_data.size(), w*4);
346                         continue ;
347                 }
348                 win->DrawScanline(y+row, x, w, scanline_data.data());
349         }
350 }
351 void HandleMessage_Blit(CClient& client, CDeserialiser& message)
352 {
353         assert(!"TODO HandleMessage_Blit");
354 }
355 void HandleMessage_DrawCtl(CClient& client, CDeserialiser& message)
356 {
357         uint16_t        win_id = message.ReadU16();
358         uint16_t        x = message.ReadU16();
359         uint16_t        y = message.ReadU16();
360         uint16_t        w = message.ReadU16();
361         uint16_t        h = message.ReadU16();
362         uint16_t        ctrl_id = message.ReadU16();
363         uint16_t        frame = message.ReadU16();
364         _SysDebug("_DrawCtl: (%i, (%i,%i) %ix%i Ctl%i frame?=0x%04x)", win_id, x, y, w, h, ctrl_id, frame);
365         
366         CWindow*        win = client.GetWindow(win_id);
367         if(!win) {
368                 throw IPC::CClientFailure("_DrawCtl: Bad window");
369         }
370         
371         const CControl* ctrl = CControl::GetByID(ctrl_id);
372         if(!ctrl) {
373                 throw IPC::CClientFailure("_DrawCtl: Invalid control ID");
374         }
375         
376         CRect   area(x,y,w,h);
377         ctrl->Render(win->m_surface, area);
378 }
379 void HandleMessage_DrawText(CClient& client, CDeserialiser& message)
380 {
381         uint16_t        win_id = message.ReadU16();
382         uint16_t        x = message.ReadU16();
383         uint16_t        y = message.ReadU16();
384         uint16_t        w = message.ReadU16();
385         uint16_t        h = message.ReadU16();
386         uint16_t        font_id = message.ReadU16();
387         ::std::string   str = message.ReadString();
388         _SysDebug("_DrawText: (%i (%i,%i) %ix%i Font%i \"%s\")", win_id, x, y, w, h, font_id, str.c_str());
389         
390         CWindow*        win = client.GetWindow(win_id);
391         if(!win) {
392                 throw IPC::CClientFailure("_DrawText: Bad window");
393         }
394         
395         // 1. Get font from client structure
396         IFontFace& fontface = client.GetFont(font_id);
397         
398         // 2. Render
399         CRect   area(x, y, w, h);
400         fontface.Render(win->m_surface, area, str, h);
401 }
402
403 void HandleMessage_FillRect(CClient& client, CDeserialiser& message)
404 {
405         uint16_t        win_id = message.ReadU16();
406         uint16_t        x = message.ReadU16();
407         uint16_t        y = message.ReadU16();
408         uint16_t        w = message.ReadU16();
409         uint16_t        h = message.ReadU16();
410         uint32_t        colour = message.ReadU32();
411         _SysDebug("_FillRect: (%i (%i,%i) %ix%i %06x)", win_id, x, y, w, h, colour);
412         
413         CWindow*        win = client.GetWindow(win_id);
414         if(!win) {
415                 throw IPC::CClientFailure("_FillRect: Bad window");
416         }
417         
418         while(h -- ) {
419                 win->FillScanline(y++, x, w, colour);
420         }
421 }
422
423 void HandleMessage_DrawRect(CClient& client, CDeserialiser& message)
424 {
425         uint16_t        win_id = message.ReadU16();
426         uint16_t        x = message.ReadU16();
427         uint16_t        y = message.ReadU16();
428         uint16_t        w = message.ReadU16();
429         uint16_t        h = message.ReadU16();
430         uint32_t        colour = message.ReadU32();
431         _SysDebug("_DrawRect: (%i (%i,%i) %ix%i %06x)", win_id, x, y, w, h, colour);
432         
433         CWindow*        win = client.GetWindow(win_id);
434         if(!win) {
435                 throw IPC::CClientFailure("_DrawRect: Bad window");
436         }
437         
438         if(h == 0) {
439         }
440         else if(h == 1) {
441                 win->FillScanline(y, x, w, colour);
442         }
443         else if(h == 2) {
444                 win->FillScanline(y++, x, w, colour);
445                 win->FillScanline(y++, x, w, colour);
446         }
447         else {
448                 win->FillScanline(y++, x, w, colour);
449                 while( h -- > 2 ) {
450                         win->FillScanline(y, x, 1, colour);
451                         win->FillScanline(y, x+w-1, 1, colour);
452                         y ++;
453                 }
454                 win->FillScanline(y++, x, w, colour);
455         }
456 }
457
458 typedef void    MessageHandler_op_t(CClient& client, CDeserialiser& message);
459
460 MessageHandler_op_t     *message_handlers[] = {
461         [IPCMSG_NULL]       = &HandleMessage_Nop,
462         [IPCMSG_REPLY]      = &HandleMessage_Reply,
463         [IPCMSG_PING]       = &HandleMessage_Ping,
464         [IPCMSG_GETGLOBAL]  = &HandleMessage_GetGlobalAttr,
465         [IPCMSG_SETGLOBAL]  = &HandleMessage_SetGlobalAttr,
466         
467         [IPCMSG_CREATEWIN]  = &HandleMessage_CreateWindow,
468         [IPCMSG_CLOSEWIN]   = &HandleMessage_DestroyWindow,
469         [IPCMSG_SETWINATTR] = &HandleMessage_SetWindowAttr,
470         [IPCMSG_GETWINATTR] = &HandleMessage_GetWindowAttr,
471         [IPCMSG_SENDIPC]    = &HandleMessage_SendIPC,   // Use the GUI server for low-bandwith IPC
472         [IPCMSG_GETWINBUF]  = &HandleMessage_GetWindowBuffer,
473         [IPCMSG_DAMAGERECT] = &HandleMessage_DamageRect,
474         [IPCMSG_PUSHDATA]   = &HandleMessage_PushData,  // to a window's buffer
475         [IPCMSG_BLIT]       = &HandleMessage_Blit,      // Copy data from one part of the window to another
476         [IPCMSG_DRAWCTL]    = &HandleMessage_DrawCtl,   // Draw a control
477         [IPCMSG_DRAWTEXT]   = &HandleMessage_DrawText,  // Draw text
478         [IPCMSG_FILLRECT]   = &HandleMessage_FillRect,  // Fill a rectangle
479         [IPCMSG_DRAWRECT]   = &HandleMessage_DrawRect,  // Draw (outline) a rectangle
480 };
481
482 void HandleMessage(CClient& client, CDeserialiser& message)
483 {
484         const unsigned int num_commands = sizeof(message_handlers)/sizeof(IPC::MessageHandler_op_t*);
485         unsigned int command = message.ReadU8();
486         if( command >= num_commands ) {
487                 // Drop, invalid command
488                 _SysDebug("HandleMessage: Command %u is invalid (out of range for %u)", command, num_commands);
489                 return ;
490         }
491         
492         (message_handlers[command])(client, message);
493 }
494
495 CClientFailure::CClientFailure(std::string&& what):
496         m_what(what)
497 {
498 }
499 const char *CClientFailure::what() const throw()
500 {
501         return m_what.c_str();
502 }
503 CClientFailure::~CClientFailure() throw()
504 {
505 }
506
507 };      // namespace IPC
508
509 IIPCChannel::~IIPCChannel()
510 {
511 }
512
513 };      // namespace AxWin
514

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