src/events/SDL_touch.c
author Sam Lantinga
Sun, 20 Nov 2011 19:38:18 -0500
changeset 6091 61fa4f3238c5
parent 6044 35448a5ea044
child 6138 4c64952a58fb
permissions -rw-r--r--
First pass at a simple drag and drop API, allowing you to accept files dropped into your application.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2011 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "SDL_config.h"
    22 
    23 /* General touch handling code for SDL */
    24 
    25 #include "SDL_events.h"
    26 #include "SDL_events_c.h"
    27 #include "../video/SDL_sysvideo.h"
    28 
    29 #include <stdio.h>
    30 
    31 
    32 static int SDL_num_touch = 0;
    33 static SDL_Touch **SDL_touchPads = NULL;
    34 
    35 
    36 /* Public functions */
    37 int
    38 SDL_TouchInit(void)
    39 {
    40   return (0);
    41 }
    42 
    43 SDL_Touch *
    44 SDL_GetTouch(SDL_TouchID id)
    45 {
    46     int index = SDL_GetTouchIndexId(id);
    47     if (index < 0 || index >= SDL_num_touch) {
    48         return NULL;
    49     }
    50     return SDL_touchPads[index];
    51 }
    52 
    53 SDL_Touch *
    54 SDL_GetTouchIndex(int index)
    55 {
    56     if (index < 0 || index >= SDL_num_touch) {
    57         return NULL;
    58     }
    59     return SDL_touchPads[index];
    60 }
    61 
    62 static int
    63 SDL_GetFingerIndexId(SDL_Touch* touch,SDL_FingerID fingerid)
    64 {
    65     int i;
    66     for(i = 0;i < touch->num_fingers;i++)
    67         if(touch->fingers[i]->id == fingerid)
    68             return i;
    69     return -1;
    70 }
    71 
    72 
    73 SDL_Finger *
    74 SDL_GetFinger(SDL_Touch* touch,SDL_FingerID id)
    75 {
    76     int index = SDL_GetFingerIndexId(touch,id);
    77     if(index < 0 || index >= touch->num_fingers)
    78         return NULL;
    79     return touch->fingers[index];
    80 }
    81 
    82 
    83 int
    84 SDL_GetTouchIndexId(SDL_TouchID id)
    85 {
    86     int index;
    87     SDL_Touch *touch;
    88 
    89     for (index = 0; index < SDL_num_touch; ++index) {
    90         touch = SDL_touchPads[index];
    91         if (touch->id == id) {
    92             return index;
    93         }
    94     }
    95     return -1;
    96 }
    97 
    98 int
    99 SDL_AddTouch(const SDL_Touch * touch, char *name)
   100 {
   101     SDL_Touch **touchPads;
   102     int index;
   103     size_t length;
   104 
   105     if (SDL_GetTouchIndexId(touch->id) != -1) {
   106         SDL_SetError("Touch ID already in use");
   107     }
   108 
   109     /* Add the touch to the list of touch */
   110     touchPads = (SDL_Touch **) SDL_realloc(SDL_touchPads,
   111                                       (SDL_num_touch + 1) * sizeof(*touch));
   112     if (!touchPads) {
   113         SDL_OutOfMemory();
   114         return -1;
   115     }
   116 
   117     SDL_touchPads = touchPads;
   118     index = SDL_num_touch++;
   119 
   120     SDL_touchPads[index] = (SDL_Touch *) SDL_malloc(sizeof(*SDL_touchPads[index]));
   121     if (!SDL_touchPads[index]) {
   122         SDL_OutOfMemory();
   123         return -1;
   124     }
   125     SDL_memcpy(SDL_touchPads[index], touch, sizeof(*touch));
   126 
   127     /* we're setting the touch properties */
   128     length = 0;
   129     length = SDL_strlen(name);
   130     SDL_touchPads[index]->focus = 0;
   131     SDL_touchPads[index]->name = SDL_malloc((length + 2) * sizeof(char));
   132     SDL_strlcpy(SDL_touchPads[index]->name, name, length + 1);   
   133 
   134     SDL_touchPads[index]->num_fingers = 0;
   135     SDL_touchPads[index]->max_fingers = 1;
   136     SDL_touchPads[index]->fingers = (SDL_Finger **) SDL_malloc(sizeof(SDL_Finger*));
   137     SDL_touchPads[index]->fingers[0] = NULL;
   138     SDL_touchPads[index]->buttonstate = 0;
   139     SDL_touchPads[index]->relative_mode = SDL_FALSE;
   140     SDL_touchPads[index]->flush_motion = SDL_FALSE;
   141     
   142     SDL_touchPads[index]->xres = (1<<(16-1));
   143     SDL_touchPads[index]->yres = (1<<(16-1));
   144     //Do I want this here? Probably
   145     SDL_GestureAddTouch(SDL_touchPads[index]);
   146 
   147     return index;
   148 }
   149 
   150 void
   151 SDL_DelTouch(SDL_TouchID id)
   152 {
   153     int index = SDL_GetTouchIndexId(id);
   154     SDL_Touch *touch = SDL_GetTouch(id);
   155 
   156     if (!touch) {
   157         return;
   158     }
   159 
   160     
   161     SDL_free(touch->name);
   162  
   163     if (touch->FreeTouch) {
   164         touch->FreeTouch(touch);
   165     }
   166     SDL_free(touch);
   167 
   168     SDL_num_touch--;
   169     SDL_touchPads[index] = SDL_touchPads[SDL_num_touch];
   170 }
   171 
   172 void
   173 SDL_TouchQuit(void)
   174 {
   175     int i;
   176 
   177     for (i = SDL_num_touch-1; i > 0 ; --i) {
   178         SDL_DelTouch(i);
   179     }
   180     SDL_num_touch = 0;
   181 
   182     if (SDL_touchPads) {
   183         SDL_free(SDL_touchPads);
   184         SDL_touchPads = NULL;
   185     }
   186 }
   187 
   188 int
   189 SDL_GetNumTouch(void)
   190 {
   191     return SDL_num_touch;
   192 }
   193 
   194 SDL_Window *
   195 SDL_GetTouchFocusWindow(SDL_TouchID id)
   196 {
   197     SDL_Touch *touch = SDL_GetTouch(id);
   198 
   199     if (!touch) {
   200         return 0;
   201     }
   202     return touch->focus;
   203 }
   204 
   205 void
   206 SDL_SetTouchFocus(SDL_TouchID id, SDL_Window * window)
   207 {
   208     int index = SDL_GetTouchIndexId(id);
   209     SDL_Touch *touch = SDL_GetTouch(id);
   210     int i;
   211     SDL_bool focus;
   212 
   213     if (!touch || (touch->focus == window)) {
   214         return;
   215     }
   216 
   217     /* See if the current window has lost focus */
   218     if (touch->focus) {
   219         focus = SDL_FALSE;
   220         for (i = 0; i < SDL_num_touch; ++i) {
   221             SDL_Touch *check;
   222             if (i != index) {
   223                 check = SDL_touchPads[i];
   224                 if (check && check->focus == touch->focus) {
   225                     focus = SDL_TRUE;
   226                     break;
   227                 }
   228             }
   229         }
   230         if (!focus) {
   231             SDL_SendWindowEvent(touch->focus, SDL_WINDOWEVENT_LEAVE, 0, 0);
   232         }
   233     }
   234 
   235     touch->focus = window;
   236 
   237     if (touch->focus) {
   238         focus = SDL_FALSE;
   239         for (i = 0; i < SDL_num_touch; ++i) {
   240             SDL_Touch *check;
   241             if (i != index) {
   242                 check = SDL_touchPads[i];
   243                 if (check && check->focus == touch->focus) {
   244                     focus = SDL_TRUE;
   245                     break;
   246                 }
   247             }
   248         }
   249         if (!focus) {
   250             SDL_SendWindowEvent(touch->focus, SDL_WINDOWEVENT_ENTER, 0, 0);
   251         }
   252     }
   253 }
   254 
   255 int 
   256 SDL_AddFinger(SDL_Touch* touch,SDL_Finger *finger)
   257 {
   258     int index;
   259     SDL_Finger **fingers;
   260     //printf("Adding Finger...\n");
   261     if (SDL_GetFingerIndexId(touch,finger->id) != -1) {
   262         SDL_SetError("Finger ID already in use");
   263         }
   264 
   265     /* Add the touch to the list of touch */
   266     if(touch->num_fingers  >= touch->max_fingers){
   267                 //printf("Making room for it!\n");
   268                 fingers = (SDL_Finger **) SDL_realloc(touch->fingers,
   269                                                      (touch->num_fingers + 1) * sizeof(SDL_Finger *));
   270                 touch->max_fingers = touch->num_fingers+1;
   271                 if (!fingers) {
   272                         SDL_OutOfMemory();
   273                         return -1;
   274                 } else {
   275                         touch->max_fingers = touch->num_fingers+1;
   276                         touch->fingers = fingers;
   277                 }
   278         }
   279 
   280     index = touch->num_fingers;
   281     //printf("Max_Fingers: %i Index: %i\n",touch->max_fingers,index);
   282 
   283     touch->fingers[index] = (SDL_Finger *) SDL_malloc(sizeof(SDL_Finger));
   284     if (!touch->fingers[index]) {
   285         SDL_OutOfMemory();
   286         return -1;
   287     }
   288     *(touch->fingers[index]) = *finger;
   289     touch->num_fingers++;
   290 
   291     return index;
   292 }
   293 
   294 int
   295 SDL_DelFinger(SDL_Touch* touch,SDL_FingerID fingerid)
   296 {
   297     int index = SDL_GetFingerIndexId(touch,fingerid);
   298     SDL_Finger* finger = SDL_GetFinger(touch,fingerid);
   299 
   300     if (!finger) {
   301         return -1;
   302     }
   303  
   304 
   305     SDL_free(finger);
   306     touch->num_fingers--;
   307     touch->fingers[index] = touch->fingers[touch->num_fingers];
   308     return 0;
   309 }
   310 
   311 
   312 int
   313 SDL_SendFingerDown(SDL_TouchID id, SDL_FingerID fingerid, SDL_bool down, 
   314                    float xin, float yin, float pressurein)
   315 {
   316     int posted;
   317         Uint16 x;
   318         Uint16 y;
   319         Uint16 pressure;
   320         SDL_Finger *finger;
   321 
   322     SDL_Touch* touch = SDL_GetTouch(id);
   323 
   324     if(!touch) {
   325       return SDL_TouchNotFoundError(id);
   326     }
   327 
   328     
   329     //scale to Integer coordinates
   330     x = (Uint16)((xin+touch->x_min)*(touch->xres)/(touch->native_xres));
   331     y = (Uint16)((yin+touch->y_min)*(touch->yres)/(touch->native_yres));
   332     pressure = (Uint16)((yin+touch->pressure_min)*(touch->pressureres)/(touch->native_pressureres));
   333     
   334     finger = SDL_GetFinger(touch,fingerid);
   335     if(down) {
   336         if(finger == NULL) {
   337             SDL_Finger nf;
   338             nf.id = fingerid;
   339             nf.x = x;
   340             nf.y = y;
   341             nf.pressure = pressure;
   342             nf.xdelta = 0;
   343             nf.ydelta = 0;
   344             nf.last_x = x;
   345             nf.last_y = y;
   346             nf.last_pressure = pressure;
   347             nf.down = SDL_FALSE;
   348             if(SDL_AddFinger(touch,&nf) < 0) return 0;
   349             finger = SDL_GetFinger(touch,fingerid);
   350         }
   351         else if(finger->down) return 0;
   352         if(xin < touch->x_min || yin < touch->y_min) return 0; //should defer if only a partial input
   353         posted = 0;
   354         if (SDL_GetEventState(SDL_FINGERDOWN) == SDL_ENABLE) {
   355             SDL_Event event;
   356             event.tfinger.type = SDL_FINGERDOWN;
   357             event.tfinger.touchId = id;
   358             event.tfinger.x = x;
   359             event.tfinger.y = y;
   360             event.tfinger.pressure = pressure;
   361             event.tfinger.state = touch->buttonstate;
   362             event.tfinger.windowID = touch->focus ? touch->focus->id : 0;
   363             event.tfinger.fingerId = fingerid;
   364             posted = (SDL_PushEvent(&event) > 0);
   365         }
   366         if(posted) finger->down = SDL_TRUE;
   367         return posted;
   368     }
   369     else {
   370         if(finger == NULL) {
   371             SDL_SetError("Finger not found.");
   372             return 0;
   373         }      
   374         posted = 0;
   375         if (SDL_GetEventState(SDL_FINGERUP) == SDL_ENABLE) {
   376             SDL_Event event;
   377             event.tfinger.type = SDL_FINGERUP;
   378             event.tfinger.touchId =  id;
   379             event.tfinger.state = touch->buttonstate;
   380             event.tfinger.windowID = touch->focus ? touch->focus->id : 0;
   381             event.tfinger.fingerId = fingerid;
   382             //I don't trust the coordinates passed on fingerUp
   383             event.tfinger.x = finger->x; 
   384             event.tfinger.y = finger->y;
   385             event.tfinger.dx = 0;
   386             event.tfinger.dy = 0;
   387 
   388             if(SDL_DelFinger(touch,fingerid) < 0) return 0;
   389             posted = (SDL_PushEvent(&event) > 0);
   390         }        
   391         return posted;
   392     }
   393 }
   394 
   395 int
   396 SDL_SendTouchMotion(SDL_TouchID id, SDL_FingerID fingerid, int relative, 
   397                     float xin, float yin, float pressurein)
   398 {
   399     SDL_Touch *touch;
   400     SDL_Finger *finger;
   401     int posted;
   402     Sint16 xrel, yrel;
   403     Uint16 x;
   404     Uint16 y;
   405     Uint16 pressure;
   406     
   407     touch = SDL_GetTouch(id);
   408     if (!touch) {
   409       return SDL_TouchNotFoundError(id);
   410     }
   411 
   412     //scale to Integer coordinates
   413     x = (Uint16)((xin+touch->x_min)*(touch->xres)/(touch->native_xres));
   414     y = (Uint16)((yin+touch->y_min)*(touch->yres)/(touch->native_yres));
   415     pressure = (Uint16)((yin+touch->pressure_min)*(touch->pressureres)/(touch->native_pressureres));
   416     if(touch->flush_motion) {
   417         return 0;
   418     }
   419     
   420     finger = SDL_GetFinger(touch,fingerid);
   421     if(finger == NULL || !finger->down) {
   422         return SDL_SendFingerDown(id,fingerid,SDL_TRUE,xin,yin,pressurein);        
   423     } else {
   424         /* the relative motion is calculated regarding the last position */
   425         if (relative) {
   426             xrel = x;
   427             yrel = y;
   428             x = (finger->last_x + x);
   429             y = (finger->last_y + y);
   430         } else {
   431             if(xin < touch->x_min) x = finger->last_x; /*If movement is only in one axis,*/
   432             if(yin < touch->y_min) y = finger->last_y; /*The other is marked as -1*/
   433             if(pressurein < touch->pressure_min) pressure = finger->last_pressure;
   434             xrel = x - finger->last_x;
   435             yrel = y - finger->last_y;
   436             //printf("xrel,yrel (%i,%i)\n",(int)xrel,(int)yrel);
   437         }
   438         
   439         /* Drop events that don't change state */
   440         if (!xrel && !yrel) {
   441 #if 0
   442             printf("Touch event didn't change state - dropped!\n");
   443 #endif
   444             return 0;
   445         }
   446         
   447         /* Update internal touch coordinates */
   448         
   449         finger->x = x;
   450         finger->y = y;
   451         
   452         /*Should scale to window? Normalize? Maintain Aspect?*/
   453         //SDL_GetWindowSize(touch->focus, &x_max, &y_max);
   454         
   455         /* make sure that the pointers find themselves inside the windows */
   456         /* only check if touch->xmax is set ! */
   457         /*
   458           if (x_max && touch->x > x_max) {
   459           touch->x = x_max;
   460           } else if (touch->x < 0) {
   461           touch->x = 0;
   462           }
   463           
   464           if (y_max && touch->y > y_max) {
   465           touch->y = y_max;
   466           } else if (touch->y < 0) {
   467           touch->y = 0;
   468           }
   469         */
   470         finger->xdelta = xrel;
   471         finger->ydelta = yrel;
   472         finger->pressure = pressure;
   473         
   474         
   475         
   476         /* Post the event, if desired */
   477         posted = 0;
   478         if (SDL_GetEventState(SDL_FINGERMOTION) == SDL_ENABLE) {
   479             SDL_Event event;
   480             event.tfinger.type = SDL_FINGERMOTION;
   481             event.tfinger.touchId = id;
   482             event.tfinger.fingerId = fingerid;
   483             event.tfinger.x = x;
   484             event.tfinger.y = y;
   485             event.tfinger.dx = xrel;
   486             event.tfinger.dy = yrel;            
   487                 
   488             event.tfinger.pressure = pressure;
   489             event.tfinger.state = touch->buttonstate;
   490             event.tfinger.windowID = touch->focus ? touch->focus->id : 0;
   491             posted = (SDL_PushEvent(&event) > 0);
   492         }
   493         finger->last_x = finger->x;
   494         finger->last_y = finger->y;
   495         finger->last_pressure = finger->pressure;
   496         return posted;
   497     }
   498 }
   499 
   500 int
   501 SDL_SendTouchButton(SDL_TouchID id, Uint8 state, Uint8 button)
   502 {
   503     SDL_Touch *touch;
   504     int posted;
   505     Uint32 type;
   506 
   507     
   508     touch = SDL_GetTouch(id);
   509     if (!touch) {
   510       return SDL_TouchNotFoundError(id);
   511     }
   512 
   513     /* Figure out which event to perform */
   514     switch (state) {
   515     case SDL_PRESSED:
   516         if (touch->buttonstate & SDL_BUTTON(button)) {
   517             /* Ignore this event, no state change */
   518             return 0;
   519         }
   520         type = SDL_TOUCHBUTTONDOWN;
   521         touch->buttonstate |= SDL_BUTTON(button);
   522         break;
   523     case SDL_RELEASED:
   524         if (!(touch->buttonstate & SDL_BUTTON(button))) {
   525             /* Ignore this event, no state change */
   526             return 0;
   527         }
   528         type = SDL_TOUCHBUTTONUP;
   529         touch->buttonstate &= ~SDL_BUTTON(button);
   530         break;
   531     default:
   532         /* Invalid state -- bail */
   533         return 0;
   534     }
   535 
   536     /* Post the event, if desired */
   537     posted = 0;
   538     if (SDL_GetEventState(type) == SDL_ENABLE) {
   539         SDL_Event event;
   540         event.type = type;
   541         event.tbutton.touchId = touch->id;
   542         event.tbutton.state = state;
   543         event.tbutton.button = button;
   544         event.tbutton.windowID = touch->focus ? touch->focus->id : 0;
   545         posted = (SDL_PushEvent(&event) > 0);
   546     }
   547     return posted;
   548 }
   549 
   550 char *
   551 SDL_GetTouchName(SDL_TouchID id)
   552 {
   553     SDL_Touch *touch = SDL_GetTouch(id);
   554     if (!touch) {
   555         return NULL;
   556     }
   557     return touch->name;
   558 }
   559 
   560 int SDL_TouchNotFoundError(SDL_TouchID id) {
   561   //int i;
   562   SDL_SetError("ERROR: Cannot send touch on non-existent device with id: %li make sure SDL_AddTouch has been called\n",id);
   563 #if 0
   564   printf("ERROR: There are %i touches installed with Id's:\n",SDL_num_touch);
   565   for(i=0;i < SDL_num_touch;i++) {
   566     printf("ERROR: %li\n",SDL_touchPads[i]->id);
   567   }
   568 #endif
   569   return 0;
   570 }
   571 /* vi: set ts=4 sw=4 expandtab: */