Removed process argument to PROCESS_CallUserSignalProc.
[wine/multimedia.git] / ipc / dde_proc.c
blobea80e7e1ab1a567a82b4428ac368f20266acb936
1 /***************************************************************************
2 * Copyright 1995, Technion, Israel Institute of Technology
3 * Electrical Eng, Software Lab.
4 * Author: Michael Veksler.
5 ***************************************************************************
6 * File: dde_proc.c
7 * Purpose : DDE signals and processes functionality for DDE
8 ***************************************************************************
9 */
10 #ifdef CONFIG_IPC
12 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
13 #define msgbuf mymsg
14 #endif
16 #include <sys/time.h>
17 #include <unistd.h>
18 #include <stdlib.h>
19 #include <signal.h>
20 #include <errno.h>
21 #include <sys/msg.h>
22 #include "windef.h"
23 #include "win.h"
24 #include "shm_semaph.h"
25 #include "shm_main_blk.h"
26 #include "dde_proc.h"
27 #include "dde_mem.h"
28 #include "dde.h"
29 #include "debugtools.h"
30 #include "xmalloc.h"
32 DECLARE_DEBUG_CHANNEL(dde)
33 DECLARE_DEBUG_CHANNEL(msg)
35 int curr_proc_idx= -1;
37 enum stop_wait_op stop_wait_op=CONT;
38 int had_SIGUSR2 = 0;
39 sigjmp_buf env_get_ack;
40 sigjmp_buf env_wait_x;
42 #define IDX_TO_HWND(idx) (0xfffe - (idx))
43 #define HWND_TO_IDX(wnd) (0xfffe - (wnd))
44 #define DDE_WIN_INFO(win) ( main_block->windows[HWND_TO_IDX(win)] )
45 #define DDE_WIN2PROC(win) ( DDE_WIN_INFO(win).proc_idx )
46 #define DDE_IsRemoteWindow(win) ( (win)<0xffff && (win)>=(0xffff-DDE_PROCS))
47 #define DDE_SEND 1
48 #define DDE_POST 2
49 #define DDE_ACK 3
50 #define DDE_MSG_SIZE sizeof(MSG16)
51 #define FREE_WND (WORD)(-2)
52 #define DELETED_WND (WORD)(-3)
53 static char *msg_type[4]={"********", "DDE_SEND", "DDE_POST", "DDE_ACK"};
55 struct msg_dat {
56 struct msgbuf dat;
57 char filler[DDE_MSG_SIZE];
58 } ;
60 typedef struct fifo_element {
61 int value;
62 struct fifo_element *next;
63 } fifo_element;
65 struct fifo {
66 fifo_element *first; /* first element in the fifo or NULL */
67 fifo_element *last; /* last element in the fifo or NULL */
69 static struct fifo fifo = {NULL,NULL};
71 void dde_proc_delete(int proc_idx);
73 void dde_proc_add_fifo(int val)
75 fifo_element *created;
77 created= (fifo_element*) xmalloc( sizeof(fifo_element) );
78 created->value = val;
79 created->next = NULL;
81 if (fifo.first==NULL)
82 fifo.first= created;
83 else
84 fifo.last->next= created;
85 fifo.last = created;
88 /* get an item from the fifo, and return it.
89 * If fifo is empty, return -1
91 int dde_proc_shift_fifo()
93 int val;
94 fifo_element *deleted;
96 if (fifo.first == NULL)
97 return -1;
99 deleted= fifo.first;
100 val= deleted->value;
101 fifo.first= deleted->next;
102 if (fifo.first == NULL)
103 fifo.last= NULL;
105 free(deleted);
106 return val;
109 static void print_dde_message(char *desc, MSG16 *msg);
111 /* This should be run only when main_block is first allocated. */
112 void dde_proc_init(dde_proc proc)
114 int proc_num;
116 for (proc_num=0 ; proc_num<DDE_PROCS ; proc_num++, proc++) {
117 proc->msg=-1;
118 proc->sem=-1;
119 proc->shmid=-1;
120 proc->pid=-1;
124 /* add current process to the list of processes */
125 void dde_proc_add(dde_proc procs)
127 dde_proc proc;
128 int proc_idx;
129 TRACE_(dde)("(..)\n");
130 shm_write_wait(main_block->sem);
132 /* find free proc_idx and allocate it */
133 for (proc_idx=0, proc=procs ; proc_idx<DDE_PROCS ; proc_idx++, proc++)
134 if (proc->pid==-1)
135 break; /* found! */
137 if (proc_idx<DDE_PROCS) { /* got here beacuse a free was found ? */
138 dde_msg_setup(&proc->msg);
139 proc->pid=getpid();
140 curr_proc_idx=proc_idx;
141 shm_sem_init(&proc->sem);
143 else {
144 fflush(stdout);
145 WARN_(dde)("Can't allocate process\n");
147 shm_write_signal(main_block->sem);
150 /* wait for dde - acknowledge message - or timout */
151 static BOOL get_ack()
153 struct timeval timeout;
154 int size;
155 struct msg_dat ack_buff;
157 /* timeout after exactly one seconf */
158 timeout.tv_sec = 1;
159 timeout.tv_usec = 0;
161 sigsetjmp(env_get_ack, 1);
162 /* get here after normal execution, or after siglongjmp */
164 do { /* loop to wait for DDE_ACK */
165 had_SIGUSR2=0;
166 stop_wait_op=CONT; /* sensitive code: disallow siglongjmp */
167 size= msgrcv( main_block->proc[curr_proc_idx].msg , &ack_buff.dat,
168 1, DDE_ACK, IPC_NOWAIT);
169 if (size>=0) {
170 TRACE_(msg)("get_ack: received DDE_ACK message\n");
171 return TRUE;
173 if (DDE_GetRemoteMessage()) {
174 had_SIGUSR2=1; /* might have recieved SIGUSR2 */
176 stop_wait_op=STOP_WAIT_ACK; /* allow siglongjmp */
178 } while (had_SIGUSR2); /* loop if SIGUSR2 was recieved */
180 /* siglongjmp should be enabled at this moment */
181 select( 0, NULL, NULL, NULL, &timeout );
182 stop_wait_op=CONT; /* disallow further siglongjmp */
184 /* timeout !! (otherwise there would have been a siglongjmp) */
185 return FALSE;
188 /* Transfer one message to a given process */
189 static BOOL DDE_DoOneMessage (int proc_idx, int size, struct msgbuf *msgbuf)
191 dde_proc proc= &main_block->proc[ proc_idx ];
194 if (proc_idx == curr_proc_idx)
195 return FALSE;
197 if (kill(proc->pid,0) < 0) {
198 /* pid does not exist, or not our */
199 dde_proc_delete(proc_idx);
200 return FALSE;
203 if (TRACE_ON(dde) || WARN_ON_dde) {
204 MSG16 *msg=(MSG16*) &msgbuf->mtext;
205 char *title;
206 if (msgbuf->mtype==DDE_SEND)
207 title="sending dde:";
208 else if (msgbuf->mtype==DDE_POST)
209 title="posting dde:";
210 else
211 title=NULL;
212 if (title)
213 print_dde_message(title, msg);
214 else
215 WARN_(dde)("Unknown message type=0x%lx\n", msgbuf->mtype);
217 TRACE_(msg)("to proc_idx=%d (pid=%d), queue=%u\n",
218 proc_idx, proc->pid, (unsigned)proc->msg);
219 if ( proc->msg != -1) {
220 TRACE_(msg)("doing...(type=%s)\n", msg_type[msgbuf->mtype]);
221 size=msgsnd (proc->msg, msgbuf, size, 0);
223 if (size<0) {
224 fflush(stdout);
225 perror("msgsnd");
227 kill(proc->pid,SIGUSR2); /* tell the process there is a message */
229 TRACE_(msg)("Trying to get acknowledgment from msg queue=%d\n",
230 proc->msg);
231 Yield16(); /* force task switch, and */
232 /* acknowledgment sending */
233 if (get_ack()) {
234 return TRUE;
235 } else {
236 fflush(stdout);
237 WARN_(dde)("get_ack: DDE_DoOneMessage: timeout\n");
238 return FALSE;
241 else {
242 WARN_(msg)("message not sent, target has no message queue\n");
243 return FALSE;
247 /* Do some sort of premitive hash table */
248 static HWND16 HWND_Local2Remote(HWND16 orig)
250 int dde_wnd_idx;
251 int deleted_idx= -1;
252 WND_DATA *tested;
253 WND_DATA *deleted= NULL;
254 int i;
256 dde_wnd_idx= orig % DDE_WINDOWS;
257 for ( i=0 ; i < DDE_WINDOWS ; i++, dde_wnd_idx++) {
258 if (dde_wnd_idx >= DDE_WINDOWS)
259 dde_wnd_idx -= DDE_WINDOWS; /* wrap-around */
261 tested= &main_block->windows[ dde_wnd_idx ];
262 if (tested->proc_idx == FREE_WND)
263 break;
265 if (deleted == NULL && tested->proc_idx == DELETED_WND) {
266 deleted= tested;
267 deleted_idx= dde_wnd_idx;
268 } else if (tested->wnd == orig && tested->proc_idx == curr_proc_idx) {
269 return IDX_TO_HWND(dde_wnd_idx);
272 if (deleted != NULL) { /* deleted is preferable */
273 /* free item, allocate it */
274 deleted->proc_idx= curr_proc_idx;
275 deleted->wnd = orig;
276 return IDX_TO_HWND(deleted_idx);
278 if (tested->proc_idx == FREE_WND) {
279 tested->proc_idx= curr_proc_idx;
280 tested->wnd = orig;
281 return IDX_TO_HWND(dde_wnd_idx);
284 WARN_(dde)("Can't map any more windows to DDE windows\n");
285 return 0;
288 static BOOL DDE_DoMessage( MSG16 *msg, int type )
290 int proc_idx;
292 MSG16 *remote_message;
293 struct msg_dat msg_dat;
294 BOOL success;
296 if (msg->wParam == 0)
297 return FALSE;
299 if (main_block==NULL) {
300 if (msg->message >= WM_DDE_FIRST && msg->message <= WM_DDE_LAST)
301 DDE_IPC_init();
302 else
303 return FALSE;
307 if (msg->wParam == (HWND16)-1)
308 return FALSE;
310 if ( ! DDE_IsRemoteWindow(msg->hwnd) && msg->hwnd!= (HWND16)-1)
311 return FALSE;
313 TRACE_(msg)("(hwnd=0x%x,msg=0x%x,..) - %s\n",
314 (int)msg->hwnd,(int)msg->message,msg_type[type]);
317 TRACE_(msg)("(hwnd=0x%x,msg=0x%x,..) -- HWND_BROADCAST !\n",
318 (int)msg->hwnd,(int)msg->message);
319 remote_message=(void*)&msg_dat.dat.mtext;
321 memcpy(remote_message, msg, sizeof(*msg));
322 remote_message->wParam= HWND_Local2Remote(msg->wParam);
323 if (remote_message->wParam == 0)
324 return FALSE;
326 msg_dat.dat.mtype=type;
328 if (msg->hwnd == (HWND16)-1) {
329 success= FALSE;
330 for ( proc_idx=0; proc_idx < DDE_PROCS ; proc_idx++) {
331 if (proc_idx == curr_proc_idx)
332 continue;
333 if (main_block->proc[ proc_idx ].msg != -1)
334 success|=DDE_DoOneMessage(proc_idx, DDE_MSG_SIZE, &msg_dat.dat);
336 return success;
337 } else {
338 return DDE_DoOneMessage(DDE_WIN2PROC(msg->hwnd), DDE_MSG_SIZE,
339 &msg_dat.dat);
343 BOOL DDE_SendMessage( MSG16 *msg)
345 return DDE_DoMessage(msg, DDE_SEND);
348 BOOL DDE_PostMessage( MSG16 *msg)
350 return DDE_DoMessage(msg, DDE_POST);
354 void dde_proc_send_ack(HWND16 wnd, BOOL val) {
355 int proc,msg;
357 static struct msgbuf msg_ack={DDE_ACK,{'0'}};
359 proc=DDE_WIN2PROC(wnd);
360 msg=main_block->proc[proc].msg;
361 TRACE_(msg)("sending ACK to wnd=%4x, proc=%d,msg=%d, pid=%d\n",
362 wnd,proc,msg,main_block->proc[proc].pid);
364 msg_ack.mtext[0]=val;
365 msgsnd (msg, &msg_ack, 1, 0);
366 kill(main_block->proc[proc].pid, SIGUSR2);
369 /* return true (non zero) if had a remote message */
370 #undef DDE_GetRemoteMessage
372 int DDE_GetRemoteMessage()
374 static int nesting=0; /* to avoid infinite recursion */
376 MSG16 *remote_message;
377 int size;
378 struct msg_dat msg_dat;
379 BOOL was_sent; /* sent/received */
380 BOOL passed;
381 WND *wndPtr;
383 if (curr_proc_idx==-1) /* do we have DDE initialized ? */
384 return 0;
386 if (nesting>10) {
387 fflush(stdout);
388 ERR_(msg)("suspecting infinite recursion, exiting");
389 return 0;
392 remote_message=(void*)&msg_dat.dat.mtext;
394 /* test for SendMessage */
395 size= msgrcv( main_block->proc[curr_proc_idx].msg , &msg_dat.dat,
396 DDE_MSG_SIZE, DDE_SEND, IPC_NOWAIT);
398 if (size==DDE_MSG_SIZE) { /* is this a correct message (if any) ?*/
399 was_sent=TRUE;
400 TRACE_(msg)("DDE:receive sent message. msg=%04x wPar=%04x"
401 " lPar=%08lx\n",
402 remote_message->message, remote_message->wParam,
403 remote_message->lParam);
404 } else {
405 size= msgrcv( main_block->proc[curr_proc_idx].msg , &msg_dat.dat,
406 DDE_MSG_SIZE, DDE_POST, IPC_NOWAIT);
408 if (size==DDE_MSG_SIZE) { /* is this a correct message (if any) ?*/
409 was_sent=FALSE;
410 TRACE_(msg)("DDE:receive posted message. "
411 "msg=%04x wPar=%04x lPar=%08lx\n",
412 remote_message->message, remote_message->wParam,
413 remote_message->lParam);
415 else
416 return 0; /* no DDE message found */
419 /* At this point we are sure that there is a DDE message,
420 * was_sent is TRUE is the message was sent, and false if it was posted
423 nesting++;
425 if (TRACE_ON(dde)) {
426 char *title;
427 if (was_sent)
428 title="receive sent dde:";
429 else
430 title="receive posted dde:";
431 print_dde_message(title, remote_message);
434 if (remote_message->hwnd != (HWND16) -1 ) {
435 HWND16 dde_window= DDE_WIN_INFO(remote_message->hwnd).wnd;
436 /* we should know exactly where to send the message (locally)*/
437 if (was_sent) {
438 TRACE_(dde)("SendMessage(wnd=0x%04x, msg=0x%04x, wPar=0x%04x,"
439 "lPar=0x%08x\n", dde_window, remote_message->message,
440 remote_message->wParam, (int)remote_message->lParam);
442 /* execute the recieved message */
443 passed= SendMessage16(dde_window, remote_message->message,
444 remote_message->wParam, remote_message->lParam);
446 /* Tell the sended, that the message is here */
447 dde_proc_send_ack(remote_message->wParam, passed);
449 else {
450 passed= PostMessage16(dde_window, remote_message->message,
451 remote_message->wParam, remote_message->lParam);
452 if (passed == FALSE) {
453 /* Tell the sender, that the message is here, and failed */
454 dde_proc_send_ack(remote_message->wParam, FALSE);
456 else {
457 /* ack will be sent later, at the first peek/get message */
458 dde_proc_add_fifo(remote_message->wParam);
461 nesting--;
462 return 1;
465 /* iterate through all the windows */
466 for (wndPtr = WIN_FindWndPtr(GetTopWindow(GetDesktopWindow()));
467 wndPtr != NULL;
468 WIN_UpdateWndPtr(&wndPtr,wndPtr->next))
470 if (wndPtr->dwStyle & WS_POPUP || wndPtr->dwStyle & WS_CAPTION) {
471 if (was_sent)
472 SendMessage16( wndPtr->hwndSelf, remote_message->message,
473 remote_message->wParam, remote_message->lParam );
474 else
475 PostMessage16( wndPtr->hwndSelf, remote_message->message,
476 remote_message->wParam, remote_message->lParam );
477 } /* if */
478 } /* for */
480 /* replay with DDE_ACK after broadcasting in DDE_GetRemoteMessage */
481 dde_proc_send_ack(remote_message->wParam, TRUE);
483 nesting--;
484 return 1;
487 int dde_reschedule()
489 int ack_wnd;
491 ack_wnd= dde_proc_shift_fifo();
492 if (ack_wnd != -1) {
493 dde_proc_send_ack(ack_wnd, TRUE);
494 usleep(10000); /* force unix task switch */
495 return 1;
497 return 0;
499 void dde_msg_setup(int *msg_ptr)
501 *msg_ptr= msgget (IPC_PRIVATE, IPC_CREAT | 0700);
502 if (*msg_ptr==-1)
503 perror("dde_msg_setup fails to get message queue");
506 /* do we have dde handling in the window ?
507 * If we have, atom usage will make this instance of wine set up
508 * it's IPC stuff.
510 void DDE_TestDDE(HWND16 hwnd)
512 static in_test = 0;
513 if (in_test++) return;
514 if (main_block != NULL) {
515 in_test--;
516 return;
518 TRACE_(msg)("(0x%04x)\n", hwnd);
519 if (hwnd==0)
520 hwnd=-1;
521 /* just send a message to see how things are going */
522 SendMessage16( hwnd, WM_DDE_INITIATE, 0, 0);
523 in_test--;
526 void dde_proc_delete(int proc_idx)
528 dde_proc_done(&main_block->proc[proc_idx]);
530 void stop_wait(int a)
533 had_SIGUSR2=1;
534 switch(stop_wait_op) {
535 case STOP_WAIT_ACK:
536 siglongjmp(env_get_ack,1);
537 break; /* never reached */
538 case STOP_WAIT_X:
539 siglongjmp(env_wait_x,1);
540 break; /* never reached */
541 case CONT:
542 /* do nothing */
546 static void print_dde_message(char *desc, MSG16 *msg)
548 /* extern const char *MessageTypeNames[];*/
549 extern int debug_last_handle_size;
550 WORD wStatus,hWord;
551 void *ptr;
552 DDEACK *ddeack;
553 DDEADVISE *ddeadvise;
554 DDEDATA *ddedata;
555 DDEPOKE *ddepoke;
556 dbg_decl_str(dde, 2048);
558 if (is_dde_handle(msg->lParam & 0xffff) )
559 ptr=DDE_AttachHandle(msg->lParam&0xffff, NULL);
560 else
561 ptr =NULL;
562 wStatus=LOWORD(msg->lParam);
563 hWord=HIWORD(msg->lParam);
565 dsprintf(dde,"%s", desc);
566 dsprintf(dde,"%04x %04x==%s %04x %08lx ",
567 msg->hwnd, msg->message,"",/*MessageTypeNames[msg->message],*/
568 msg->wParam, msg->lParam);
569 switch(msg->message) {
570 case WM_DDE_INITIATE:
571 case WM_DDE_REQUEST:
572 case WM_DDE_EXECUTE:
573 case WM_DDE_TERMINATE:
574 /* nothing to do */
575 break;
576 case WM_DDE_ADVISE:
577 /* DDEADVISE: hOptions in WM_DDE_ADVISE message */
578 if (ptr) {
579 ddeadvise=ptr;
580 dsprintf(dde,"fDeferUpd=%d,fAckReq=%d,cfFormat=0x%x",
581 ddeadvise->fDeferUpd, ddeadvise->fAckReq,
582 ddeadvise->cfFormat);
583 } else
584 dsprintf(dde,"NO-DATA");
585 dsprintf(dde," atom=0x%x",hWord);
586 break;
588 case WM_DDE_UNADVISE:
589 dsprintf(dde,"format=0x%x, atom=0x%x",wStatus,hWord);
590 break;
591 case WM_DDE_ACK:
592 ddeack=(DDEACK*)&wStatus;
593 dsprintf(dde,"bAppReturnCode=%d,fBusy=%d,fAck=%d",
594 ddeack->bAppReturnCode, ddeack->fBusy, ddeack->fAck);
595 if (ddeack->fAck)
596 dsprintf(dde,"(True)");
597 else
598 dsprintf(dde,"(False)");
599 break;
601 case WM_DDE_DATA:
602 if (ptr) {
603 ddedata=ptr;
604 dsprintf(dde,"fResponse=%d,fRelease=%d,"
605 "fAckReq=%d,cfFormat=0x%x,value=\"%.*s\"",
606 ddedata->fResponse, ddedata->fRelease,
607 ddedata->fAckReq, ddedata->cfFormat,
608 debug_last_handle_size- (int)sizeof(*ddedata)+1,
609 ddedata->Value);
610 } else
611 dsprintf(dde,"NO-DATA");
612 dsprintf(dde," atom=0x%04x",hWord);
613 break;
615 case WM_DDE_POKE:
616 if (ptr) {
617 ddepoke=ptr;
618 dsprintf(dde,"fRelease=%d,cfFormat=0x%x,value[0]='%c'",
619 ddepoke->fRelease, ddepoke->cfFormat, ddepoke->Value[0]);
620 } else
621 dsprintf(dde,"NO-DATA");
622 dsprintf(dde," atom=0x%04x",hWord);
623 break;
625 TRACE_(dde)("%s\n", dbg_str(dde));
628 void dde_proc_done(dde_proc proc)
630 if (proc->msg != -1)
631 msgctl(proc->msg, IPC_RMID, NULL);
632 proc->msg=-1;
633 proc->pid=-1;
634 shm_delete_chain(&proc->shmid);
635 shm_sem_done(&proc->sem);
638 /* delete entry, if old junk */
639 void dde_proc_refresh(dde_proc proc)
641 if (proc->pid == -1)
642 return;
644 if (kill(proc->pid, 0) != -1)
645 return;
647 /* get here if entry non empty, and the process does not exist */
648 dde_proc_done(proc);
651 void dde_wnd_setup()
653 int i;
655 for (i=0 ; i < DDE_WINDOWS ; i++)
656 main_block->windows[i].proc_idx = FREE_WND;
659 static BOOL DDE_ProcHasWindows(int proc_idx)
661 WND_DATA *tested;
662 int i;
664 for ( i=0 ; i < DDE_WINDOWS ; i++) {
665 tested= &main_block->windows[ i ];
667 if (tested->proc_idx == proc_idx)
668 return TRUE;
670 return FALSE;
672 /* Look for hwnd in the hash table of DDE windows,
673 * Delete it from there. If there are no more windows for this
674 * process, remove the process from the DDE data-structure.
675 * If there are no more processes - delete the whole DDE struff.
677 * This is inefficient, but who cares for the efficiency of this rare
678 * operation...
680 void DDE_DestroyWindow(HWND16 hwnd)
682 int dde_wnd_idx;
683 WND_DATA *tested;
684 int i;
686 if (main_block == NULL)
687 return;
689 dde_wnd_idx= hwnd % DDE_WINDOWS;
691 for ( i=0 ; i < DDE_WINDOWS ; i++, dde_wnd_idx++) {
692 if (dde_wnd_idx >= DDE_WINDOWS)
693 dde_wnd_idx -= DDE_WINDOWS; /* wrap-around */
695 tested= &main_block->windows[ dde_wnd_idx ];
696 if (tested->proc_idx == FREE_WND)
697 return; /* No window will get deleted here */
699 if (tested->wnd == hwnd && tested->proc_idx == curr_proc_idx) {
700 dde_reschedule();
701 tested->proc_idx= DELETED_WND;
702 if (DDE_ProcHasWindows( curr_proc_idx ))
703 return;
704 while (dde_reschedule()) /* make sure there are no other */
705 /* processes waiting for acknowledgment */
707 dde_proc_delete( curr_proc_idx );
708 if (DDE_no_of_attached() == 1)
709 shm_delete_all(-1);
710 else {
711 shmdt( (void *) main_block);
712 main_block= NULL;
714 return;
719 #endif /* CONFIG_IPC */