home *** CD-ROM | disk | FTP | other *** search
/ ARM Club 3 / TheARMClub_PDCD3.iso / programs / desktop / newbar / Source / NewBar / c / hacky < prev    next >
Text File  |  1998-08-07  |  12KB  |  328 lines

  1.  
  2. /* hacky.c */
  3.  
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <stdio.h>
  7. #include "swis.h"
  8. #include "OS:wimp.h"
  9. #include "OS:wimpspriteop.h"
  10. #include "OS:wimpreadsysinfo.h"
  11. #include "Dreamscape:task.h"
  12. #include "Dreamscape:x.h"
  13.  
  14. #ifdef MemCheck_MEMCHECK
  15. #include "MemCheck:memcheck.h"
  16. #endif
  17.  
  18. #include "ibarpatch.h"
  19. #include "debug.h"
  20.  
  21.  
  22. #define MODULE_NAME    "hacky"
  23.  
  24.  
  25. /* This function extracts the text and sprite info given an icon block and
  26.    its icon flags.  If necessary, it copies indirected text or sprite data
  27.    from the other task. */
  28. ibarpatch_icon_properties *ibarpatch_extract_info(wimp_t task,
  29.     wimp_icon_flags flags, const wimp_icon_data *data)
  30. {
  31.   os_error *e;
  32.   ibarpatch_icon_properties *i = malloc(sizeof(ibarpatch_icon_properties));
  33.   if(!i) x_throw_message(x_msg_memory());
  34.  
  35.   DEBUGF("Extracting icon info: ");
  36.  
  37.   /* Use Wimp area by default, but the value may be changed later. */
  38.   i->sprite.area = 0;
  39.   /* By default, sprite pointers are not exchanged. */
  40.   i->sprite.is_ptr = 0;
  41.  
  42.   /* So, what are our choices of icon type? */
  43.   if(flags & wimp_ICON_INDIRECTED) {
  44.     if(flags & wimp_ICON_TEXT) {
  45.  
  46.       /* Handle indirected, text + sprite icons */
  47.       /* Assumes that these always have a sprite */
  48.  
  49.       const int overflow = 40, validation_max = 256;
  50.       char *c, *buffer, *t;
  51.  
  52.       DEBUGF("text+sprite icon");
  53.  
  54.       /* Copy indirected text */
  55.       c = i->text = malloc(data->indirected_text.size + overflow + 1);
  56.       if(!c) x_throw_message(x_msg_memory());
  57.       t = c + data->indirected_text.size + overflow;
  58.       e = xwimp_transfer_block(task, (byte *) data->indirected_text.text,
  59.         dscape_task_get_handle(), (byte *) c,
  60.         data->indirected_text.size + overflow);
  61.       if(!e) {
  62.         while(*c >= 32 && c < t) ++c;
  63.         *c = 0;
  64.       }
  65.       else {
  66.         strcpy(i->text, "<failed>");
  67.         DEBUGF1(", text transfer error `%s'", e->errmess);
  68.       }
  69.       DEBUGF2(", text `%s' (at 0x%p)", i->text, data->indirected_text.text);
  70.  
  71.       /* Copy validation string and extract sprite name */
  72.       c = i->sprite.id.name = malloc(16);
  73.       t = buffer = malloc(validation_max);
  74.       if(!c || !t) x_throw_message(x_msg_memory());
  75.         /* Horrid I know, because it doesn't free it properly on failure */
  76.       if(xwimp_transfer_block(task, (byte *)
  77.     data->indirected_text.validation, dscape_task_get_handle(),
  78.     (byte *) buffer, validation_max)) { goto sprite_failed; }
  79.       buffer[validation_max-1] = 0; /* Add emergency terminator */
  80.       DEBUGF2(", validation string `%s' (at 0x%p)", buffer,
  81.         data->indirected_text.validation);
  82.       /* Search until we find an `s' (sprite) term */
  83.       while(*t != 's' && *t != 'S' &&        /* An `s' term? */
  84.         (t == buffer || t[-1] != ';') &&    /* Start of a term? */
  85.         *t >= 32)                /* End of string? */
  86.       { if(*t == '\\') t += 2; else ++t; }    /* Handle escape char */
  87.       /* If we found an `s' term, copy the sprite name */
  88.       if(*t == 's' || *t == 'S') {
  89.         char *begin = ++t;
  90.         /* If the icon has its selected flag set, skip to the second
  91.            sprite name */
  92.         if(flags & wimp_ICON_SELECTED) {
  93.           DEBUGF(", icon selected");
  94.           while(*t != ';' && *t != ',' && *t > 32) ++t;
  95.           if(*t != ',') t = begin; else ++t;
  96.         }
  97.         while(*t != ';' && *t != ',' && *t > 32)
  98.           *c++ = *t++;
  99.         *c = 0;
  100.       }
  101.       else {
  102.         sprite_failed: /* If that failed, use a default sprite */
  103.         strcpy(c, "badibicon");
  104.       }
  105.       free(buffer);
  106.       DEBUGF1(", sprite `%s'\n", i->sprite.id.name);
  107.     }
  108.     else {
  109.       /* Handle indirected, sprite-only icons */
  110.       DEBUGF("indirected sprite icon");
  111.  
  112.       /* Sprite is referred to by name, so copy the name */
  113.       if(data->indirected_sprite.area == wimpspriteop_AREA ||
  114.         data->indirected_sprite.size) {
  115.         char *c = i->sprite.id.name = malloc(16);
  116.         if(!c) x_throw_message(x_msg_memory());
  117.         if(xwimp_transfer_block(task, (byte *) data->indirected_sprite.id,
  118.         dscape_task_get_handle(), (byte *) c, 16))
  119.             { free(i->sprite.id.name); goto ind_failed; }
  120.         while(*c > 32 && (c - i->sprite.id.name) < 16) ++c;
  121.         *c = 0;
  122.         DEBUGF1(", sprite name `%s'", i->sprite.id.name);
  123.       }
  124.  
  125.       /* Ho ho, this is fun.  The app has a sprite in its own sprite pool,
  126.          so we must copy it into our own wimpslot. */
  127.       if(data->indirected_sprite.area != wimpspriteop_AREA) {
  128.         /* We do this the suboptimal way.  First, we transfer the first 4
  129.            bytes of the area to find out how large it is.  Then we transfer
  130.            the whole thing.  There are always two transfers, which would be
  131.            the unavoidable worst case anyway.  (Guesses could be made to
  132.            reduce it to one transfer, but this is simpler.) */
  133.         osspriteop_area *sdata;
  134.     int size;
  135.     if(xwimp_transfer_block(task, (byte *) data->indirected_sprite.area,
  136.         dscape_task_get_handle(), (byte *) &size, 4))
  137.             { goto ind_failed; }
  138.         DEBUGF1(", copying sprite area (size %i)", size);
  139.         if(size <= 16) {
  140.           /* If the size was read as zero (or a stupidly low value), set it
  141.              defensively to 10k.  This is necessary for SwiftJPEG, which
  142.              doesn't bother filling in its size (broken!).  However, well
  143.              done for providing source so I could check my suspicions! */
  144.           size = 10 * 1024;
  145.           DEBUGF1(", fiddled size to %i", size);
  146.         }
  147.     sdata = malloc(size);
  148.     if(!sdata) x_throw_message(x_msg_memory());
  149.     if(xwimp_transfer_block(task, (byte *) data->indirected_sprite.area,
  150.         dscape_task_get_handle(), (byte *) sdata, size))
  151.             { free(sdata); goto ind_failed; }
  152.     i->sprite.area = sdata;
  153.  
  154.     /* Sprite is *not* referred to by name, so fiddle the sprite pointer
  155.        so that it points within our sprite area. */
  156.     if(!data->indirected_sprite.size) {
  157.       i->sprite.id.ptr = (osspriteop_id) ((char *) sdata +
  158.         (int) data->indirected_sprite.id -
  159.         (int) data->indirected_sprite.area);
  160.           i->sprite.is_ptr = 1;
  161.           DEBUGF(", uses sprite pointer");
  162.     }
  163.       }
  164.       /* If a data transfer failed, we jump here and fill in a default
  165.          sprite name. */
  166.       if(0) {
  167.       ind_failed:
  168.         i->sprite.id.name = malloc(16);
  169.         if(!i->sprite.id.name) x_throw_message(x_msg_memory());
  170.         strcpy(i->sprite.id.name, "badibicon");
  171.         i->sprite.is_ptr = 0;
  172.         i->sprite.area = 0;
  173.       }
  174.       i->text = 0;
  175.       DEBUGF("\n");
  176.     }
  177.   }
  178.   else {
  179.     /* Handle non-indirected icons */
  180.     /* Assumes that these are going to be sprite-only */
  181.  
  182.     /* Copy the sprite name */
  183.     char *c = i->sprite.id.name = malloc(16);
  184.     if(!c) x_throw_message(x_msg_memory());
  185.     memcpy(c, data->sprite, 12);
  186.     while(*c > 32 && (c - i->sprite.id.name) < 12) ++c;
  187.     *c = 0;
  188.     i->text = 0;
  189.     DEBUGF1("non-indirected sprite icon, name `%s'\n", i->sprite.id.name);
  190.   }
  191.   return i;
  192. }
  193.  
  194. /* Frees the data returned by ibarpatch_extract_info(), all except for the
  195.    sprite area (which is typically passed to the iconbar gadget to keep). */
  196. void ibarpatch_free_info(ibarpatch_icon_properties *info)
  197. {
  198.   if(!info) return;
  199.  
  200.   free(info->text);
  201.   if(!info->sprite.is_ptr) free(info->sprite.id.name);
  202.   free(info);
  203. }
  204.  
  205.  
  206. #define MAGIC_TASK_WORD 0x4B534154
  207.  
  208. /* This function allows you to alter a window's flags.  Via hackery, this
  209.    will work on any Wimp version.  Like Wimp_SetIconState, this takes flags
  210.    to clear and flags to invert.  Does not mind if the window is open or
  211.    not. */
  212. void set_window_flags(wimp_w window, wimp_window_flags eor_bits,
  213.     wimp_window_flags clear_bits)
  214. {
  215.   int wimp_vsn;
  216.   LOGFUNC(set_window_flags);
  217.  
  218.   /* This all depends on the Wimp version. */
  219.   wimp_vsn = wimpreadsysinfo_version();
  220.   /* Wimp 3.68 is the version that comes with RISC OS 3.7. */
  221.   if(wimp_vsn > 368) {
  222.     /* We are running under a new version (the Nested Wimp).  This lets us
  223.        change window flags via a SWI.  Thanks to an anonymous informant
  224.        who provided documentation. :-) */
  225.     wimp_window_state block;
  226.     wimp_window_flags old_flags;
  227.     wimp_w parent_window;
  228.     unsigned align_flags;
  229.     DEBUGF("Using Nested Wimp SWI to set window flags\n");
  230.  
  231.     /* Read the current window block. */
  232.     block.w = window;
  233.     _swi(Wimp_GetWindowState, _IN(1)|_IN(2)|_OUT(3)|_OUT(4), &block,
  234.     MAGIC_TASK_WORD, &parent_window, &align_flags);
  235.     old_flags = block.flags;
  236.     block.flags = (block.flags &~ clear_bits) ^ eor_bits;
  237.  
  238.     /* Set the flags with new-form Wimp_OpenWindow.  The block from
  239.        Wimp_GetWindowInfo corresponds to that required by Wimp_OpenWindow. */
  240.     /* If the window wasn't open before, we don't want it open now, so send
  241.        it to very bottom of stack. */
  242.     if(!(old_flags & wimp_WINDOW_OPEN)) block.next = wimp_HIDDEN;
  243.     _swi(Wimp_OpenWindow, _IN(1)|_IN(2)|_IN(3)|_IN(4), &block,
  244.     MAGIC_TASK_WORD, parent_window,
  245.     align_flags | 1 /* Set window flags */);
  246.  
  247.     /* If the window was closed before, make sure it's closed now. */
  248.     if(!(old_flags & wimp_WINDOW_OPEN)) wimp_close_window(window);
  249.   }
  250.   else {
  251.     /* We are running on an old Wimp version.  This is where it gets fun.
  252.        We have to hack the Wimp's workspace to change the flags; luckily
  253.        we only have to do this on known versions.  Thanks to Stewart Brodie
  254.        for his explanation of how to do this. */
  255.     int offset;
  256.     char *wp = ((char *) window) - 1;
  257.     wimp_window *block;
  258.     DEBUGF("Using hackery to set window flags\n");
  259.  
  260. #ifdef MemCheck_MEMCHECK
  261.     MemCheck_RegisterMiscBlock(wp, sizeof(wimp_window) + 100);
  262. #endif
  263.  
  264.     /* Check window is valid. */
  265.     if(!window ||
  266.       ((int) window < 0 && (int) window > -10) ||
  267.       ((int) window & 3) != 1 ||
  268.       wp[0] != 'W' || wp[1] != 'i' || wp[2] != 'n' || wp[3] != 'd')
  269.     x_throw_message("Illegal window handle when hacking workspace");
  270.  
  271.     /* Find offset of window block. */
  272.     if(wimp_vsn >= 350 && wimp_vsn <= 368) offset = 80;
  273.     else if(wimp_vsn >= 310 && wimp_vsn <= 324) offset = 72;
  274.     else { offset = 0; /* Suppress warning */
  275.     x_throw_message("Can't hack workspace on this Wimp version"); }
  276.     block = (wimp_window *) (wp + offset);
  277.  
  278.     /* Make some checks */
  279.     if(!(block->title_flags & (wimp_ICON_TEXT | wimp_ICON_FILLED) &&
  280.      ~block->title_flags & wimp_ICON_SPRITE &&
  281.      block->scroll_outer == 3 &&
  282.      block->scroll_inner == 1))
  283.       x_throw_message("Can't hack Wimp workspace "
  284.         "(reason: block doesn't look like a window)");
  285.  
  286.     /* On a debug build, make more comprehensive checks. */
  287. #ifdef DEBUG
  288.     {
  289.       wimp_window_info got;
  290.       got.w = window;
  291.       wimp_get_window_info_header_only(&got);
  292.       /* Check our block with what the Wimp tells us.
  293.          Don't bother checking it all. */
  294.       if(!(got.title_flags == block->title_flags &&
  295.            got.work_flags == block->work_flags &&
  296.            got.sprite_area == block->sprite_area &&
  297.            got.next == block->next))
  298.     x_throw_message("Can't hack Wimp workspace (check failed)");
  299.     }
  300. #endif
  301.  
  302.     /* Now fiddle with the Wimp's data. */
  303.     block->flags = (block->flags &~ clear_bits) ^ eor_bits;
  304.  
  305. #ifdef MemCheck_MEMCHECK
  306.     MemCheck_UnRegisterMiscBlock(wp);
  307. #endif
  308.   }
  309. }
  310.  
  311. /* Returns the task handle of the task owning the given Wimp window (and
  312.    icon).  Returns 0 if unsuccessful.  Icon handle will usually be -1. */
  313. wimp_t task_handle_from_window(wimp_w window, int icon)
  314. {
  315.   wimp_t owning_task;
  316.   wimp_message msg;
  317.   LOGFUNC(task_handle_from_window);
  318.   if(!window) return 0;
  319.  
  320.   /* Send a fake message to the other window. */
  321.   msg.size = 5*4;
  322.   msg.my_ref = msg.your_ref = 0;
  323.   msg.action = -1;
  324.   if(xwimp_send_message_to_window(wimp_USER_MESSAGE_ACKNOWLEDGE, &msg,
  325.     window, icon, &owning_task)) return 0;
  326.   return owning_task;
  327. }
  328.