chiark / gitweb /
Initial revision
[ssr] / StraySrc / Libraries / Steel / c / xfersend
1 /* Title: -> c.xfersend
2  * Purpose: generalised data transfer to a concurrent wimp program.
3  */
4
5 /*----- Licensing note ----------------------------------------------------*
6  *
7  * This file is part of Straylight's Steel library.
8  *
9  * Steel is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2, or (at your option)
12  * any later version.
13  *
14  * Steel is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Steel.  If not, write to the Free Software Foundation,
21  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22  */
23
24 #define BOOL int
25 #define TRUE 1
26 #define FALSE 0
27
28 #include <string.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31
32 #include "trace.h"
33 #include "os.h"
34 #include "bbc.h"
35 #include "wimp.h"
36 #include "wimpt.h"
37 #include "win.h"
38 #include "dbox.h"
39 #include "xfersend.h"
40 #include "fileicon.h"
41 #include "werr.h"
42 #include "menu.h"
43 #include "event.h"
44 #include "msgs.h"
45 #include "swis.h"
46
47 static int rcvbufsize ;
48 static int xfersend__msgid;
49 static xfersend_saveproc xfersend__saveproc;
50 static xfersend_sendproc xfersend__sendproc;
51 static xfersend_printproc xfersend__printproc;
52 static int xfersend__filetype;
53 static void *xfersend__savehandle;
54 static int xfersend__estsize;
55 static wimp_t xfersend__receiver ;
56 static BOOL xfersend__fileissafe ;
57 static char xfersend__filename[256];
58
59 static wimp_mousestr xfersend__mousestr;
60 static wimp_msgstr xfersend__msg;
61 static BOOL xfersend__close;
62 static wimp_w xfersend__w;
63 static wimp_i xfersend__i;
64
65 static BOOL xfersend__finishedXfer;
66
67 static void xfersend__iconDel(wimp_w w,wimp_i i,BOOL del)
68 {
69   if (wimpt_options() & wimpt_OREMSAVEICON)
70   {
71     if (del)
72       wimp_set_icon_state(w,i,(1<<23)+(1<<7),(1<<23)+(1<<7));
73     else
74       wimp_set_icon_state(w,i,0,(1<<23)+(1<<7));
75   }
76 }
77
78 void xfersend_close_on_xfer(BOOL do_we_close, wimp_w w)
79 {
80   xfersend__close = do_we_close;
81   xfersend__w = w;
82 }
83
84 static void xfersend__winclose(void)
85 {
86   wimp_eventdata e;
87   e.o.w = xfersend__w;
88   wimpt_noerr(wimp_sendwmessage(wimp_ECLOSE, (wimp_msgstr*) &e, e.o.w, -1));
89 }
90
91
92 static BOOL xfersend__unknowns(wimp_eventstr *e, void *handle) {
93   handle = handle ;
94   tracef1("xfersend raw event %i.\n", e->e);
95   switch (e->e) {
96   case wimp_EUSERDRAG:
97     tracef0("drag event received.\n");
98     {
99       wimp_get_point_info(&xfersend__mousestr);
100       if (xfersend__mousestr.w == -1) {
101         tracef0("drag to no window: has no effect.\n");
102         xfersend__finishedXfer=TRUE;
103         /* do nothing */
104       } else {
105         wimp_msgstr msg;
106
107         tracef1("drag to window %i: offer data.\n", xfersend__mousestr.w);
108         /* msg.hdr.size = sizeof(wimp_msghdr) + sizeof(wimp_msgdatasave); */
109         msg.hdr.task = xfersend__mousestr.w;
110         msg.hdr.your_ref = 0;
111         msg.hdr.action = wimp_MDATASAVE;
112         msg.data.datasave.w = xfersend__mousestr.w;
113         msg.data.datasave.i = xfersend__mousestr.i;
114         msg.data.datasave.x = xfersend__mousestr.x;
115         msg.data.datasave.y = xfersend__mousestr.y;
116         msg.data.datasave.type = xfersend__filetype;
117         msg.data.datasave.estsize = xfersend__estsize;
118         {
119           int  tail;
120           char name[256];
121           strncpy(name,xfersend__filename,  256);
122           tail = strlen(name); /* point at the zero */
123           while (tail > 0 && name[tail-1] != '.' && name[tail-1] != ':')
124                tail--;
125
126           strncpy(msg.data.datasave.leaf, name + tail, 210);
127           msg.data.dataload.name[211] = 0; /* Avoid boundcheck warning */
128           msg.hdr.size = (48+strlen(msg.data.datasave.leaf)) & ~3;
129           tracef1("suggest leaf '%s'.\n", (int) &msg.data.datasave.leaf[0]);
130         };
131         wimpt_noerr(wimp_sendwmessage(
132            wimp_ESENDWANTACK, &msg, xfersend__mousestr.w, xfersend__mousestr.i));
133         xfersend__msgid = msg.hdr.my_ref; /* filled in by wimp. */
134         /* We still get unknown events, so we'll get the reply sometime. */
135       };
136     };
137     return TRUE;
138
139   case wimp_ESEND:
140   case wimp_ESENDWANTACK:
141
142 #if TRACE
143     tracef("xfersend msg %x received: %i %i.\n",
144             e->data.msg.hdr.action,e->data.msg.hdr.your_ref,xfersend__msgid);
145 #endif
146
147     if (e->data.msg.hdr.your_ref == xfersend__msgid)
148      switch (e->data.msg.hdr.action)
149      {
150       case wimp_MRAMFETCH:
151        if (xfersend__sendproc != 0)
152        {
153         xfersend__fileissafe = FALSE ;
154
155         /* He wants to do an in-core transfer, and we can do this. */
156         /* Note that this can't be other than the first response, as others
157            are grabbed by sendbuf */
158
159         tracef0("ram transfer starting.\n");
160
161         /* Prepare the reply record. */
162         xfersend__msg = e->data.msg;
163         xfersend__msg.hdr.your_ref = xfersend__msg.hdr.my_ref;
164         xfersend__msg.hdr.action = wimp_MRAMTRANSMIT;
165         xfersend__msg.data.ramtransmit.addr = e->data.msg.data.ramfetch.addr;
166         xfersend__msg.data.ramtransmit.nbyteswritten = 0; /* so far. */
167         rcvbufsize = e->data.msg.data.ramfetch.nbytes;
168
169         xfersend__receiver = e->data.msg.hdr.task ;
170         /* the copy in xfersend__msg.hdr.task is overwritten by the Wimp
171            message sending */
172
173         if (xfersend__sendproc(xfersend__savehandle, &rcvbufsize))
174         {
175          /* See sendbuf for all the real work for this case... */
176          tracef0("The send succeeded; send final RAMTRANSMIT.\n");
177
178          /* We may have transferred some data but not yet told the
179          other end about it. xfersend__msg contains a final RAMTRANSMIT,
180          which does not quite fill his buffer (or we'd have sent it already)
181          thus signalling to him that the transfer is over. */
182
183          wimpt_noerr(wimp_sendmessage(
184            wimp_ESEND,
185            &xfersend__msg,
186            xfersend__receiver));
187          xfersend__finishedXfer=TRUE;
188          if(xfersend__close) xfersend__winclose();
189          else xfersend__iconDel(xfersend__w,xfersend__i,FALSE);
190         }
191         else
192         {
193           tracef0("the send failed.\n");
194           xfersend__iconDel(xfersend__w,xfersend__i,FALSE);
195           xfersend__finishedXfer=TRUE;
196         };
197         return TRUE;
198        }
199        else
200        {
201          if (getenv("Wimp$Scrap")==0)
202            xfersend__finishedXfer=TRUE;
203       /*** The other application reports an error, and I don't get my ***
204        *** no-acknowledgement  message to tell me to abort  the  data ***
205        *** transfer.                                                  ***/
206        }
207        break ;
208
209       case wimp_MPrintFile:       /* was dropped on a printer application */
210        if (xfersend__printproc != 0)
211        {
212         int res ;
213
214         tracef0("print request acceptable\n");
215         xfersend__fileissafe = FALSE ;
216
217         res = xfersend__printproc(&e->data.msg.data.print.name[0],
218                                       xfersend__savehandle) ;
219
220         xfersend__msg = e->data.msg;
221         xfersend__msg.hdr.your_ref = xfersend__msg.hdr.my_ref;
222         xfersend__msg.hdr.action =
223            res >= 0 ? wimp_MDATALOAD : wimp_MWillPrint;
224         xfersend__msg.data.print.type = res ;  /* in case it's been saved */
225         wimpt_noerr(wimp_sendmessage(
226            wimp_ESENDWANTACK,
227            &xfersend__msg,
228            xfersend__receiver));
229         xfersend__msgid=xfersend__msg.hdr.my_ref;
230         if (res==xfersend_printPrinted)
231         {
232           xfersend__finishedXfer=TRUE;
233           if(xfersend__close) xfersend__winclose();
234           else xfersend__iconDel(xfersend__w,xfersend__i,FALSE);
235         }
236         else if (res==xfersend_printFailed)
237           xfersend__iconDel(xfersend__w,xfersend__i,FALSE);
238         return TRUE;
239        }
240        break ;
241
242       case wimp_MDATALOADOK:
243         xfersend__finishedXfer=TRUE;
244         if(xfersend__close) xfersend__winclose();
245         else xfersend__iconDel(xfersend__w,xfersend__i,FALSE);
246         return (TRUE);
247         break;
248
249       case wimp_MDATASAVEOK:
250       {
251         tracef4("datasaveok %i %i %i %i.\n",
252           e->data.msg.hdr.size,
253           e->data.msg.hdr.task,
254           e->data.msg.hdr.your_ref,
255           e->data.msg.hdr.my_ref);
256         tracef4("datasaveok %x %x %x %x.\n",
257           e->data.msg.data.words[0],
258           e->data.msg.data.words[1],
259           e->data.msg.data.words[2],
260           e->data.msg.data.words[3]);
261         tracef1("it's the datasaveok, to file '%s'.\n",
262                  (int) &e->data.msg.data.datasaveok.name[0]);
263
264         tracef1("save to filename '%s'.\n",
265                 (int) &e->data.msg.data.datasaveok.name[0]);
266
267         xfersend__fileissafe = e->data.msg.data.datasaveok.estsize >= 0 ;
268
269         if (xfersend__saveproc(&e->data.msg.data.datasaveok.name[0],
270                              xfersend__savehandle))
271         {
272           tracef0("the save succeeded: send dataload\n");
273
274           xfersend__msg = e->data.msg;
275                                  /* sets hdr.size, data.w,i,x,y, size, name */
276           xfersend__msg.hdr.your_ref = e->data.msg.hdr.my_ref;
277           xfersend__msg.hdr.action = wimp_MDATALOAD;
278           xfersend__msg.data.dataload.type = xfersend__filetype ;
279           wimpt_noerr(wimp_sendmessage(
280             wimp_ESENDWANTACK,
281             &xfersend__msg,
282             e->data.msg.hdr.task));
283           xfersend__msgid=xfersend__msg.hdr.my_ref;
284
285         } else
286         {
287           /* he has already reported the error: nothing more to do. */
288           tracef0("save was not successful.\n");
289           xfersend__iconDel(xfersend__w,xfersend__i,FALSE);
290           xfersend__finishedXfer=TRUE;
291         };
292         return TRUE;
293       }
294      }
295     return FALSE ;      /* unknown not dealt with */
296
297   case wimp_EACK:
298     if (e->data.msg.hdr.my_ref == xfersend__msgid)
299     {
300       switch (e->data.msg.hdr.action)
301       {
302         case wimp_MDATALOAD:
303           {
304             /* It looks as if he hasn't acknowledged my DATALOAD acknowledge:
305                thus it may be a loose scrap file, and must be deleted. */
306             char a[256];
307             tracef0("he hasn't ack'd our data load of temp file, so delete the file.\n");
308             werr(FALSE, msgs_lookup("xfersend1:Bad data transfer, receiver dead."));
309             sprintf(a, "%%delete %s", &xfersend__msg.data.dataload.name[0]);
310             os_cli(a) ;
311             xfersend__iconDel(xfersend__w,xfersend__i,FALSE);
312             xfersend__finishedXfer=TRUE;
313           }
314           break;
315         case wimp_MDATASAVE:
316           xfersend__finishedXfer=TRUE;
317           xfersend__iconDel(xfersend__w,xfersend__i,FALSE);
318           break;
319       }
320     };
321     return TRUE;
322   default:
323     return FALSE ;
324   };
325 }
326
327
328 static int sendbuf__state ;
329
330 static BOOL sendbuf__unknowns(wimp_eventstr *e, void *h)
331 {
332  h = h ;
333
334  tracef4("sendbuf__unknowns %d %d %d %d\n",
335           e->data.msg.hdr.my_ref, e->data.msg.hdr.your_ref,
336           xfersend__msg.hdr.your_ref, xfersend__msg.hdr.my_ref) ;
337
338  if ((e->e == wimp_ESENDWANTACK || e->e == wimp_ESEND) &&
339      e->data.msg.hdr.your_ref == xfersend__msg.hdr.my_ref &&
340      e->data.msg.hdr.action == wimp_MRAMFETCH)
341  {
342   /* Prepare xfersend__msg as the next RAMTRANSMIT. Most of
343   the fields are already set up. */
344
345   xfersend__msg.data.ramtransmit.addr = e->data.msg.data.ramfetch.addr;
346   xfersend__msg.data.ramtransmit.nbyteswritten = 0;
347   xfersend__msg.hdr.your_ref = e->data.msg.hdr.my_ref ;
348   rcvbufsize = e->data.msg.data.ramfetch.nbytes;
349
350   tracef2("RAMFETCH received: continue with buffer at %x, size %d\n",
351            (int) xfersend__msg.data.ramtransmit.addr, rcvbufsize) ;
352
353   sendbuf__state = 1 ;
354   return TRUE ;      /* We've had another RAMFETCH: off we go again */
355  }
356
357  if (e->e == wimp_EACK &&
358     e->data.msg.hdr.my_ref == xfersend__msg.hdr.my_ref)
359  {
360   sendbuf__state = 2 ;
361   tracef0("xfersend RAMTRANSMIT bounced; set failed state\n") ;
362   return TRUE ;/* our message bounced back; give up */
363  }
364
365  return FALSE ;    /* we don't want it */
366 }
367
368
369 BOOL xfersend_sendbuf(char *buffer, int size)
370 {
371
372 /* Called by his sendproc when sending things in memory. The
373 reply record is in xfersend__msg. */
374
375  tracef2("xfersend_sendbuf %i %i\n", (int) buffer, size);
376
377  /* Make the data transfer */
378  tracef3("transfer block of %d from %x to %x\n", size, (int) buffer,
379            (int) (xfersend__msg.data.ramtransmit.addr +
380             xfersend__msg.data.ramtransmit.nbyteswritten)) ;
381
382  wimpt_noerr(wimp_transferblock(
383       wimpt_task(),
384       buffer,
385       xfersend__receiver,
386       xfersend__msg.data.ramtransmit.addr +
387         xfersend__msg.data.ramtransmit.nbyteswritten,
388       size));
389
390  /* record bytes to be sent to the other end */
391  xfersend__msg.data.ramtransmit.nbyteswritten += size;
392  rcvbufsize -= size ;
393
394  /* if size != 0, there are still bytes to send. */
395
396   if (rcvbufsize > 0) return TRUE;
397
398  tracef1("xfersend message has put %d into buffer\n",size) ;
399  /* Tell him that you've done it */
400  wimpt_noerr(wimp_sendmessage(
401       wimp_ESENDWANTACK,
402       &xfersend__msg,
403       xfersend__receiver));
404
405   /* Get his reply. Poll and despatch events until get nack or message */
406
407  sendbuf__state = 0 ;
408
409  win_add_unknown_event_processor(sendbuf__unknowns, 0) ;
410  do { event_process() ; } while (sendbuf__state == 0) ;
411  win_remove_unknown_event_processor(sendbuf__unknowns, 0) ;
412
413   /* This exit happens in the cases where the buffers at each end
414       are of identical size. So, return for another call to sendbuf, or
415       so that the sendbuf procedure can return. */
416
417  return sendbuf__state != 2 ;  /* OK unless state = broken */
418 }
419
420 BOOL xfersend(int filetype, char *filename, int estsize,
421                      xfersend_saveproc saver,
422                      xfersend_sendproc sender,
423                      xfersend_printproc printer,
424                      wimp_eventstr *e, void *handle)
425 {
426       wimp_dragstr dr;
427       wimp_wstate wstate;
428       wimp_icon icon;
429       wimp_w w = e->data.but.m.w;
430       os_regset r;
431       xfersend__saveproc = saver;
432       xfersend__sendproc = sender;
433       xfersend__printproc = printer;
434       xfersend__filetype = filetype;
435       xfersend__estsize = estsize;
436       xfersend__savehandle = handle;
437       if(filename == 0)
438         strcpy(xfersend__filename, msgs_lookup("xfersend2:Selection"));
439       else
440         strncpy(xfersend__filename,filename,256);
441       if (e->e==wimp_EBUT)
442       {
443         xfersend__w = e->data.but.m.w;
444         xfersend__i = e->data.but.m.i;
445         tracef0("Initiate a drag.\n");
446         r.r[0]=161; /* Read CMOS */
447         r.r[1]=0x1c; /* Support DragASrite? */
448         wimpt_noerr(os_swix(XOS_Byte,&r));
449         wimp_get_wind_state(w, &wstate);
450         wimp_get_icon_info(w, e->data.but.m.i, &icon);
451         dr.box.x0 = wstate.o.box.x0 - wstate.o.x + icon.box.x0;
452         dr.box.y0 = wstate.o.box.y1 - wstate.o.y + icon.box.y0;
453         dr.box.x1 = wstate.o.box.x0 - wstate.o.x + icon.box.x1;
454         dr.box.y1 = wstate.o.box.y1 - wstate.o.y + icon.box.y1;
455         if ((r.r[2] & 2) && wimpt_getVersion()>=300)
456         {
457           r.r[0]=0xC5; /* Centre, whole-screen, drop-shadow */
458           r.r[1]=1;
459           r.r[2]=(int)fileicon_spriteName(filetype,filename);
460           r.r[3]=(int)&dr.box;
461           xfersend__iconDel(xfersend__w,xfersend__i,TRUE);
462           wimpt_noerr(os_swix(XDragASprite_Start,&r));
463         }
464         else
465         {
466           /* Pedestrian drag-box */
467           dr.window = w; /* not relevant. */
468           dr.type = wimp_USER_FIXED;
469           dr.parent.x0 = 0;
470           dr.parent.y0 = 0;
471           dr.parent.x1 = wimpt_scwidth();
472           dr.parent.y1 = wimpt_scheight();
473           wimp_drag_box(&dr);
474         }
475       }
476       win_add_unknown_event_processor(xfersend__unknowns, 0);
477       xfersend__finishedXfer=FALSE;
478       while (xfersend__finishedXfer==FALSE)
479         event_process();
480       win_remove_unknown_event_processor(xfersend__unknowns, 0);
481       xfersend__w=0;
482       return TRUE;
483 }
484
485
486 BOOL xfersend_file_is_safe()
487 {
488  return xfersend__fileissafe ;
489 }
490
491 void xfersend_set_fileissafe(BOOL value)
492 {
493   xfersend__fileissafe = value;
494 }
495
496 /*
497  * BOOL xfersend_sendBlock(void *block,size_t length,int *maxbuf)
498  *
499  * Use
500  *  Sends a block to xfersend_sendbuf() a chunk at a time.
501  *
502  * Parameters
503  *  void *block == pointer to the block
504  *  size_t length == length of block
505  *  int *maxbuf == pointer to the length of the destination task's buffer
506  *
507  * Returns
508  *  TRUE if successful, FALSE if it failed.  If this returns FALSE, you must
509  *  return FALSE immediately.
510  */
511
512 BOOL xfersend_sendBlock(void *block,int length,int *maxbuf)
513 {
514   char *p=(char *)block;
515   int left=length;
516   int send;
517   while (left)
518   {
519     if (left<=(*maxbuf))
520       send=left;
521     else
522       send=*maxbuf;
523     if (xfersend_sendbuf(p,send)==FALSE)
524       return (FALSE);
525     left-=send;
526     p+=send;
527   }
528   return (TRUE);
529 }
530
531 /* end xfersend.c */