home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 244.lha / KeyClick / keyclick.c < prev    next >
C/C++ Source or Header  |  1989-05-03  |  17KB  |  515 lines

  1. /* Keyclick V1.1 by Mike M. Duppong  [3/29/88] */
  2. /* Last modified: 11/25/88 */
  3. /* Written in Aztec C V3.6A */
  4. /* Compile: cc keyclick.c */
  5. /* Link: ln keyclick.o -lc */
  6. /* Note: all of my routine and variable names are in lower case letters
  7.  * only.  The Amiga system calls almost always contain at least one
  8.  * capital letter.  In using this convention, I hope to simplify the
  9.  * distinction between my calls and the system calls. */
  10.  
  11. #include <exec/types.h>
  12. #include <exec/memory.h>
  13. #include <exec/ports.h>
  14. #include <exec/errors.h>
  15. #include <exec/devices.h>
  16. #include <exec/execbase.h>
  17. #include <intuition/intuition.h>
  18. #include <devices/input.h>
  19. #include <devices/inputevent.h>
  20. #include <devices/audio.h>
  21. #include <functions.h>
  22.  
  23. #define SCREENTITLE "Keyclick V1.1 by Mike Duppong"
  24. #define WINDOWTITLE "Keyclick"
  25. #define HANDLER_PRIORITY 51 /* Priority of input handler */
  26. #define AUDIO_PRIORITY -90 /* Priority of click sound */
  27. #define TASK_PRIORITY 40L /* Priority of this task */
  28. #define WAVELENGTH 16L  /* # of bytes in waveform */
  29. #define PERIOD 1L       /* Waveform period */
  30. #define CYCLES 25L      /* Number of waveform cycles */
  31. #define WINDOWX 0       /* Window's LeftEdge */
  32. #define WINDOWY 13      /* Window's TopEdge */
  33. #define WINDOWW 174     /* Window width */
  34. #define WINDOWH 24      /* Window height */
  35. #define VOLGADX 13      /* Volume gadget's LeftEdge */
  36. #define VOLGADY 12      /* Volume gadget's TopEdge */
  37. #define VOLGADW 128     /* Volume gadget's width */
  38. #define VOLGADH 8       /* Volume gadget's height */
  39. #define VOLGADID 1      /* Volume gadget ID */
  40. #define SWTGADX (VOLGADX + VOLGADW + 10) /* LeftEdge */
  41. #define SWTGADY 11      /* Switch gadget's TopEdge */
  42. #define SWTGADW 9       /* Switch gadget's pixel width */
  43. #define SWTGADWW ((SWTGADW + 15) / 16) /* word width */
  44. #define SWTGADD 1       /* # of planes in gadget image */
  45. #define SWTGADH 12      /* Switch gadget's height */
  46. #define SWTGADID 2      /* Switch gadget ID */
  47. #define CLUP_INPDEV 1   /* Close-up flags for cleanup() */
  48. #define CLUP_AUDDEV 2   /* Audio device */
  49. #define CLUP_HANDLER 4  /* Remove handler */
  50.  
  51. struct IOStdReq *iostdreq;
  52. struct IOAudio *ioaudio;
  53. struct Interrupt interrupt;
  54. struct MsgPort *input_port, *audio_port;
  55. struct IntuitionBase *IntuitionBase;
  56. struct Window *window;
  57. struct PropInfo prop;
  58. struct Image vol_image;
  59. struct Image onswt_image =
  60. {
  61.   0, 0,                 /* LeftEdge, TopEdge */
  62.   SWTGADW, SWTGADH, SWTGADD,/* Width, Height, Depth */
  63.   NULL,                 /* ImageData - set below */
  64.   0x1, 0,               /* PlanePick, PlaneOnOff */
  65.   NULL                  /* NextImage */
  66. };
  67. struct Image offswt_image =
  68. {
  69.   0, 0,                 /* LeftEdge, TopEdge */
  70.   SWTGADW, SWTGADH, SWTGADD,/* Width, Height, Depth */
  71.   NULL,                 /* ImageData - set below */
  72.   0x1, 0,               /* PlanePick, PlaneOnOff */
  73.   NULL                  /* NextImage */
  74. };
  75. struct Gadget switch_gadget =
  76. {
  77.   NULL,                 /* NextGadget - none */
  78.   SWTGADX, SWTGADY,     /* LeftEdge, TopEdge */
  79.   SWTGADW, SWTGADH,     /* Width, Height */
  80.   SELECTED | GADGHIMAGE | GADGIMAGE, /* Flags */
  81.   GADGIMMEDIATE | RELVERIFY | TOGGLESELECT, /* Activation */
  82.   BOOLGADGET,           /* GadgetType */
  83.   NULL,                 /* GadgetRender - set below */
  84.   NULL,                 /* SelectRender - set below */
  85.   NULL,                 /* GadgetText */
  86.   NULL,                 /* MutualExclude */
  87.   NULL,                 /* SpecialInfo */
  88.   SWTGADID,             /* GadgetID */
  89.   NULL                  /* UserData */
  90. };
  91. struct Gadget volgadget =
  92. {
  93.   &switch_gadget,       /* NextGadget */
  94.   VOLGADX, VOLGADY,     /* LeftEdge, TopEdge */
  95.   VOLGADW, VOLGADH,     /* Width, Height */
  96.   GADGHCOMP,            /* Flags */
  97.   GADGIMMEDIATE | RELVERIFY,/* Activation */
  98.   PROPGADGET,           /* GadgetType */
  99.   NULL,                 /* GadgetRender- set below */
  100.   NULL,                 /* SelectRender */
  101.   NULL,                 /* GadgetText */
  102.   NULL,                 /* MutualExclude */
  103.   (APTR)&prop,          /* SpecialInfo */
  104.   VOLGADID,             /* GadgetID */
  105.   NULL                  /* UserData */
  106. };
  107. struct NewWindow windowdef =
  108. {
  109.   WINDOWX, WINDOWY, WINDOWW, WINDOWH, /* X, Y, W, H */
  110.   0, 1,                 /* Detail/block pen */
  111.   CLOSEWINDOW | GADGETUP, /* IDCMP flags */
  112.   NOCAREREFRESH | SMART_REFRESH | ACTIVATE | WINDOWDRAG |
  113.   WINDOWDEPTH | WINDOWCLOSE, /* Flags */
  114.   &volgadget,           /* First gadget */
  115.   NULL,                 /* Check mark */
  116.   (UBYTE *)WINDOWTITLE, /* Title */
  117.   NULL,                 /* Screen */
  118.   NULL,                 /* Bitmap */
  119.   0, 0, 0, 0,           /* Min & Max Width & Height */
  120.   WBENCHSCREEN          /* Workbench window */
  121. };
  122.  
  123. UBYTE allocation[] = {1, 8, 2, 4}; /* Channel allocation */
  124. BYTE *waveform;         /* Pointer to waveform RAM */
  125. UWORD onswt_data[] =
  126. {
  127.   0x0000, 0x0800, 0x1C00, 0x1C00, 0x3E00, 0xDD80,
  128.   0x8880, 0xC180, 0x3E00, 0x0000, 0x0000, 0x0000
  129. };
  130. UWORD offswt_data[] =
  131. {
  132.   0x0000, 0x0000, 0x0000, 0x0000, 0x3C00, 0xC180,
  133.   0x8880, 0xDD80, 0x3E00, 0x1C00, 0x1C00, 0x0800
  134. };
  135. struct infostruct  /* Information passed between program and input handler */
  136. {
  137.   struct Task *taskp;
  138.   ULONG key_signal;
  139.   struct Gadget *gadget;
  140. } ih_info;
  141. int clup;
  142. int volume = 63;  /* Click volume */
  143. void handler_interface();
  144.  
  145.  
  146.  
  147. main()
  148. {
  149.   init_gadgets();
  150.   open_window();
  151.   init_sound();
  152.   init_info();
  153.   open_input_device();
  154.   add_handler();
  155.   watch();
  156.   /* cleanup() is used by watch() to exit this program. */
  157. }
  158.  
  159.  
  160. init_gadgets()
  161. {
  162.   init_slider();
  163.   init_switch();
  164. }
  165.  
  166.  
  167. init_slider()
  168. {
  169.   /* Initialize pointers to image RAM */
  170.   volgadget.GadgetRender = (APTR)&vol_image;
  171.  
  172.   /* Set slider to horizontally-moving auto-knob */
  173.   prop.Flags = AUTOKNOB | FREEHORIZ;
  174.   prop.HorizPot = 0xFFFF; /* Pegged to begin with */
  175.   prop.HorizBody = 0xFFFF / 32;
  176. }
  177.  
  178.  
  179. init_switch()
  180. {
  181.   register int i;
  182.  
  183.   /* Allocate RAM for on-switch image data */
  184.   if((onswt_image.ImageData = AllocMem((long)
  185.   (SWTGADWW * SWTGADH * SWTGADD * 2), MEMF_CHIP)) == NULL)
  186.     cleanup("Not enough memory.");
  187.  
  188.   /* Allocate RAM for off-switch image */
  189.   if((offswt_image.ImageData = AllocMem((long)
  190.   (SWTGADWW * SWTGADH * SWTGADD * 2), MEMF_CHIP)) == NULL)
  191.     cleanup("Not enough memory.");
  192.  
  193.   /* Move switch image data to CHIP memory */
  194.   for(i=0; i<SWTGADWW * SWTGADH * SWTGADD; i++)
  195.   {
  196.     *((UWORD *)(onswt_image.ImageData + i)) = onswt_data[i];
  197.     *((UWORD *)(offswt_image.ImageData + i)) = offswt_data[i];
  198.   }
  199.  
  200.   /* Point to appropriate image structures */
  201.   switch_gadget.GadgetRender = (APTR)&offswt_image;
  202.   switch_gadget.SelectRender = (APTR)&onswt_image;
  203. }
  204.  
  205.  
  206. open_window()
  207. {
  208.   short exists;
  209.  
  210.   if((IntuitionBase = OpenLibrary("intuition.library", 1L)) == NULL)
  211.     error("Can't open Intuition library.");
  212.  
  213.   /* Look for an already existing message port named "keyclick.audio."
  214.    * If it exists, another key-clicker is active in the system and this
  215.    * program will abort. */
  216.   if((ULONG)FindPort("keyclick.audio") != NULL)
  217.   {
  218.     /* Move window over a tad to avoid obscuring the original (should it
  219.      * remain unmoved) */
  220.     windowdef.LeftEdge += 5;
  221.     windowdef.TopEdge += 5;
  222.     exists = TRUE;
  223.   }
  224.   else exists = FALSE;
  225.   if((window = OpenWindow(&windowdef)) == NULL)
  226.     error("Can't open a window.");
  227.   if(exists == TRUE)
  228.     error("A key-clicker is already active!");
  229. }
  230.  
  231.  
  232. init_sound()
  233. {
  234.   register int i;
  235.  
  236.   if((ioaudio = AllocMem((long)sizeof(struct IOAudio),
  237.   MEMF_PUBLIC | MEMF_CLEAR)) == NULL)
  238.     error("Can't allocate RAM for IOAudio structure.\n");
  239.   if((audio_port = CreatePort("keyclick.audio", NULL)) == NULL)
  240.     cleanup("Can't create audio message port.");
  241.   if((OpenDevice("audio.device", NULL, ioaudio, NULL)) != NULL)
  242.     error("Audio device failed to open.");
  243.   clup |= CLUP_AUDDEV;
  244.  
  245.   if((waveform = AllocMem(WAVELENGTH, MEMF_CHIP)) == NULL)
  246.     error("Can't allocate memory for waveform.");
  247.  
  248.   for(i=0; i<WAVELENGTH; i++) /* Create a triangle waveform */
  249.   {
  250.     waveform[i / 2] = i * 127 / WAVELENGTH;
  251.     waveform[i / 2 + (WAVELENGTH / 2)] = (127 - i) * WAVELENGTH / 127;
  252.   }
  253. }
  254.  
  255.  
  256. init_info() /* Initializes the info structure */
  257. {
  258.   ih_info.taskp = FindTask(NULL); /* Pointer to this task */
  259.   SetTaskPri(ih_info.taskp, TASK_PRIORITY); /* Set appropriate priority */
  260.   ih_info.key_signal = AllocSignal(-1L); /* Get a signal to talk to in.han. */
  261.   if(ih_info.key_signal == -1)  /* Check for correct allocation */
  262.     error("Can't allocate a signal");
  263.   ih_info.gadget = &switch_gadget; /* Input handler must know about this */
  264. }
  265.  
  266.  
  267. open_input_device()
  268. {
  269.   if((input_port = CreatePort("keyclick.input", NULL)) == NULL)
  270.     error("Can't create an input device message port.");
  271.   if((iostdreq = CreateStdIO(input_port)) == NULL)
  272.     error("Can't create an IOStdReq structure.");
  273.   if((OpenDevice("input.device", NULL, iostdreq, NULL)) != NULL)
  274.     error("Cannot open input device.");
  275.   clup |= CLUP_INPDEV;
  276. }
  277.  
  278.  
  279. add_handler()
  280. {
  281.   interrupt.is_Code = handler_interface;  /* Pointer to assembly interface */
  282.   interrupt.is_Data = (APTR)&ih_info;     /* Pointer to mem needed by hand. */
  283.   interrupt.is_Node.ln_Pri = HANDLER_PRIORITY; /* Input handler priority */
  284.   iostdreq->io_Command = IND_ADDHANDLER;
  285.   iostdreq->io_Data = (APTR)&interrupt;
  286.   DoIO(iostdreq);
  287.   clup |= CLUP_HANDLER;
  288. }
  289.  
  290.  
  291. watch() /* Watches input device port for messages */
  292. {
  293.   struct IntuiMessage *msg, *dqmsg;
  294.   struct Gadget *gadget;
  295.   ULONG mclass, sig;
  296.  
  297.   /* Remove all waiting messages (if any) from window's IDCMP first */
  298.   while((dqmsg = (struct IntuiMessage *)GetMsg(window->UserPort)))
  299.     ReplyMsg(dqmsg);
  300.   SetWindowTitles(window, -1L, SCREENTITLE);
  301.   while(1)
  302.   {
  303.     /* Wait for a signal from Intuition or the custom input handler */
  304.     sig = Wait((ULONG)(1L << ih_info.key_signal) |
  305.       (ULONG)(1L << (ULONG)window->UserPort->mp_SigBit));
  306.  
  307.     /* If the signal was from the input handler... */
  308.     if(sig & (ULONG)(1L << ih_info.key_signal))
  309.       click();
  310.  
  311.     /* If the signal was from Intuition... */
  312.     if(sig & (ULONG)(1L << (ULONG)window->UserPort->mp_SigBit))
  313.     {
  314.       /* Take the message from the IDCMP */
  315.       msg = (struct IntuiMessage *) GetMsg(window->UserPort);
  316.  
  317.       /* Make a copy of the message class */
  318.       mclass = msg->Class;
  319.  
  320.       /* Also make a copy of which gadget was pressed (if any) */
  321.       gadget = (struct Gadget *)msg->IAddress;
  322.  
  323.       /* Reply to the message before processing it */
  324.       ReplyMsg(msg); /* Reply to sender */
  325.  
  326.       switch(mclass)
  327.       {
  328.         case CLOSEWINDOW: cleanup(); break; /* If close-gadget hit... */
  329.         case GADGETUP:  /* If a gadget has been pressed (let up on) */
  330.           if(gadget->GadgetID == SWTGADID)  /* Switch? */
  331.             toggle_click();  /* Yes, toggle the click */
  332.           else if(gadget->GadgetID == VOLGADID)  /* Volume slider? */
  333.             adjust_volume(); /* Yes, adjust to new volume level */
  334.           break;
  335.       }
  336.     }
  337.   }
  338. }
  339.  
  340.  
  341. toggle_click()  /* Enables/disables volume gadget */
  342. {
  343.   if(switch_gadget.Flags & (WORD)SELECTED)
  344.     OnGadget(&volgadget, window, NULL);
  345.   else
  346.     OffGadget(&volgadget, window, NULL);
  347. }
  348.  
  349.  
  350. get_audio_channel()  /* Allocates a free audio channel (if any) */
  351. {
  352.   ioaudio->ioa_Request.io_Command = ADCMD_ALLOCATE;
  353.   ioaudio->ioa_Request.io_Flags = ADIOF_NOWAIT | IOF_QUICK;
  354.   ioaudio->ioa_AllocKey = 0;    /* Generate new key */
  355.   ioaudio->ioa_Request.io_Message.mn_Node.ln_Pri = AUDIO_PRIORITY;
  356.   ioaudio->ioa_Request.io_Message.mn_ReplyPort = audio_port;
  357.   ioaudio->ioa_Data = allocation;
  358.   ioaudio->ioa_Length = (long)sizeof(allocation);
  359.   BeginIO(ioaudio);
  360. }
  361.  
  362.  
  363. free_audio_channel()  /* Frees up a previously allocated audio channel */
  364. {
  365.   ioaudio->ioa_Request.io_Command = ADCMD_FREE;
  366.   ioaudio->ioa_Request.io_Flags = IOF_QUICK | ADIOF_SYNCCYCLE;
  367.   BeginIO(ioaudio);
  368. }
  369.  
  370.  
  371. adjust_volume()  /* Record new volume in the variable "volume" */
  372. {
  373.   volume = ((long)(prop.HorizPot) * 64L) >> 16;
  374. }
  375.  
  376.  
  377. click()  /* Take care of all things required to produce a click */
  378. {
  379.   get_audio_channel();  /* Allocate a channel */
  380.   if((long)ioaudio->ioa_Request.io_Unit == NULL) /* If channel not avail...*/
  381.     return;
  382.  
  383.   /* Fill in the ioaudio structure with appropriate parameters */
  384.   ioaudio->ioa_Request.io_Command = CMD_WRITE;
  385.   ioaudio->ioa_Request.io_Flags = ADIOF_PERVOL | IOF_QUICK | ADIOF_NOWAIT;
  386.   ioaudio->ioa_Data = (UBYTE *)waveform;
  387.   ioaudio->ioa_Length = WAVELENGTH;
  388.   ioaudio->ioa_Period = PERIOD;
  389.   ioaudio->ioa_Volume = (ULONG)volume;
  390.   ioaudio->ioa_Cycles = CYCLES;
  391.  
  392.   BeginIO(ioaudio); /* Send off audio command */
  393.   WaitIO(ioaudio);  /* Wait for CMD_WRITE operation to complete */
  394.   free_audio_channel(); /* Free up the audio channel */
  395. }
  396.  
  397.  
  398. remove_handler() /* Removes the custom input handler from system list */
  399. {
  400.   iostdreq->io_Command = IND_REMHANDLER;
  401.   iostdreq->io_Data = (APTR)&interrupt;
  402.   DoIO(iostdreq);
  403. }
  404.  
  405.  
  406. struct InputEvent *input_handler(event, info_str)   /* The informer */
  407. register struct InputEvent *event;
  408. register struct infostruct *info_str;
  409. {
  410.   /* If toggle switch is in the on-position
  411.        If a RAWKEY (down-key) event is intercepted OR
  412.        a raw mouse event (button pressed)
  413.          Signal the waiting task to make a click. */
  414.  
  415.   if(info_str->gadget->Flags & (WORD)SELECTED)
  416.     if((event->ie_Class == IECLASS_RAWKEY &&
  417.     !(event->ie_Code & IECODE_UP_PREFIX)) ||
  418.     (event->ie_Class == IECLASS_RAWMOUSE &&
  419.     (event->ie_Code == IECODE_LBUTTON ||
  420.     event->ie_Code == IECODE_RBUTTON ||
  421.     event->ie_Code == IECODE_MBUTTON)))
  422.       Signal(info_str->taskp, (ULONG)(1L << info_str->key_signal));
  423.   return(event);
  424. }
  425.  
  426.  
  427. error(message)
  428. char *message;
  429. {
  430.   struct IntuiMessage *msg;
  431.   ULONG mclass = NULL;
  432.  
  433.   /* If the window opened okay, place the error message in
  434.   the Workbench screen's title bar and wait for the window
  435.   to close.  Otherwise, attempt to output the message to
  436.   a CLI window with puts(). */
  437.   if(window != NULL) /* If opened successfully... */
  438.   {
  439.     /* Turn off gadgets */
  440.     OffGadget(&volgadget, window, NULL);
  441.     OffGadget(&switch_gadget, window, NULL);
  442.     SetWindowTitles(window, -1L, message);  /* Set title to error message */
  443.     while(mclass != CLOSEWINDOW) /* Wait for a CLOSEWINDOW message */
  444.     {
  445.       WaitPort(window->UserPort); /* Wait for a message */
  446.       msg = (struct IntuiMessage *) GetMsg(window->UserPort);
  447.       mclass = msg->Class; /* Grab message class */
  448.       ReplyMsg(msg); /* Reply to message */
  449.     }
  450.   }
  451.   else puts(message); /* Try to output to CLI if window isn't open */
  452.   cleanup(); /* Do the standard cleanup from here. */
  453. }
  454.  
  455.  
  456. cleanup()
  457. {
  458.   struct Message *dqmsg;
  459.  
  460.   if(clup & CLUP_HANDLER)    /* Remove the handler from system */
  461.     remove_handler();
  462.   if(ih_info.key_signal != -1) /* Deallocate handler-to-task signal */
  463.     FreeSignal((ULONG)(1L << ih_info.key_signal));
  464.   if(clup & CLUP_INPDEV)     /* Close input device */
  465.     CloseDevice(iostdreq);
  466.   if(iostdreq)               /* Delete created structure */
  467.     DeleteStdIO(iostdreq);
  468.   if(clup & CLUP_AUDDEV)     /* Close audio device */
  469.     CloseDevice(ioaudio);
  470.   if(waveform != NULL)       /* Free waveform memory */
  471.     FreeMem(waveform, WAVELENGTH);
  472.   if(ioaudio != NULL)        /* Free IOAudio structure */
  473.     FreeMem(ioaudio, (long)sizeof(struct IOAudio));
  474.   if(input_port)             /* Remove input device message port */
  475.     DeletePort(input_port);
  476.   if(audio_port)             /* Remove audio device message port */
  477.     DeletePort(audio_port);
  478.   if(window != NULL)         /* Dequeue message port and close window */
  479.   { /* Make sure all messages are answered to free all memory */
  480.     while((dqmsg = GetMsg(window->UserPort))) ReplyMsg(dqmsg);
  481.     CloseWindow(window);
  482.   }
  483.   if(onswt_image.ImageData != NULL) /* Free ON-switch image data */
  484.     FreeMem(onswt_image.ImageData, (long)
  485.     (SWTGADWW * SWTGADH * SWTGADD * 2));
  486.   if(offswt_image.ImageData != NULL) /* Free OFF-switch image data */
  487.     FreeMem(offswt_image.ImageData, (long)
  488.     (SWTGADWW * SWTGADH * SWTGADD * 2));
  489.   if(IntuitionBase != NULL)  /* Close Intuition library */
  490.     CloseLibrary(IntuitionBase);
  491.   exit(NULL);                /* Terminate program here */
  492. }
  493.  
  494.  
  495. /* Custom assembly language input handler interface - needed for system to
  496.  * communicate with custom input handler; the system has the required
  497.  * information in registers A0 and A1.  They must be placed on the stack
  498.  * with this function so the C routine (input_handler()) can pull the
  499.  * arguments off properly */
  500. #asm
  501.   cseg
  502.   xref _input_handler
  503.   xdef _handler_interface
  504.  
  505. _handler_interface:
  506.   move.l a4,-(a7)
  507.   movem.l a0-a1,-(a7)
  508.   jsr _geta4#
  509.   jsr _input_handler
  510.   addq.l #8,a7
  511.   move.l (a7)+,a4
  512.   rts
  513. #endasm
  514.  
  515.