src/events/SDL_touch.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 15 Feb 2013 08:47:44 -0800
changeset 6885 700f1b25f77f
parent 6863 73be5b6b2a86
child 6951 7833f01322b3
permissions -rw-r--r--
Happy New Year!
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2013 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 = SDL_strlen(name);
   129     SDL_touchPads[index]->focus = 0;
   130     SDL_touchPads[index]->name = SDL_malloc((length + 2) * sizeof(char));
   131     SDL_strlcpy(SDL_touchPads[index]->name, name, length + 1);   
   132 
   133     SDL_touchPads[index]->num_fingers = 0;
   134     SDL_touchPads[index]->max_fingers = 1;
   135     SDL_touchPads[index]->fingers = (SDL_Finger **) SDL_malloc(sizeof(SDL_Finger*));
   136     SDL_touchPads[index]->fingers[0] = NULL;
   137     SDL_touchPads[index]->buttonstate = 0;
   138     SDL_touchPads[index]->relative_mode = SDL_FALSE;
   139     SDL_touchPads[index]->flush_motion = SDL_FALSE;
   140     
   141     SDL_touchPads[index]->xres = (1<<(16-1));
   142     SDL_touchPads[index]->yres = (1<<(16-1));
   143     SDL_touchPads[index]->pressureres = (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                 if (!fingers) {
   271                         SDL_OutOfMemory();
   272                         return -1;
   273                 } else {
   274                         touch->max_fingers = touch->num_fingers+1;
   275                         touch->fingers = fingers;
   276                 }
   277         }
   278 
   279     index = touch->num_fingers;
   280     //printf("Max_Fingers: %i Index: %i\n",touch->max_fingers,index);
   281 
   282     touch->fingers[index] = (SDL_Finger *) SDL_malloc(sizeof(SDL_Finger));
   283     if (!touch->fingers[index]) {
   284         SDL_OutOfMemory();
   285         return -1;
   286     }
   287     *(touch->fingers[index]) = *finger;
   288     touch->num_fingers++;
   289 
   290     return index;
   291 }
   292 
   293 int
   294 SDL_DelFinger(SDL_Touch* touch,SDL_FingerID fingerid)
   295 {
   296     int index = SDL_GetFingerIndexId(touch,fingerid);
   297     SDL_Finger* finger = SDL_GetFinger(touch,fingerid);
   298 
   299     if (!finger) {
   300         return -1;
   301     }
   302  
   303 
   304     SDL_free(finger);
   305     touch->num_fingers--;
   306     touch->fingers[index] = touch->fingers[touch->num_fingers];
   307     return 0;
   308 }
   309 
   310 
   311 int
   312 SDL_SendFingerDown(SDL_TouchID id, SDL_FingerID fingerid, SDL_bool down, 
   313                    float xin, float yin, float pressurein)
   314 {
   315     int posted;
   316         Uint16 x;
   317         Uint16 y;
   318         Uint16 pressure;
   319         SDL_Finger *finger;
   320 
   321     SDL_Touch* touch = SDL_GetTouch(id);
   322 
   323     if(!touch) {
   324       return SDL_TouchNotFoundError(id);
   325     }
   326 
   327     
   328     //scale to Integer coordinates
   329     x = (Uint16)((xin+touch->x_min)*(touch->xres)/(touch->native_xres));
   330     y = (Uint16)((yin+touch->y_min)*(touch->yres)/(touch->native_yres));
   331     pressure = (Uint16)((pressurein+touch->pressure_min)*(touch->pressureres)/(touch->native_pressureres));
   332     
   333     finger = SDL_GetFinger(touch,fingerid);
   334     if(down) {
   335         if(finger == NULL) {
   336             SDL_Finger nf;
   337             nf.id = fingerid;
   338             nf.x = x;
   339             nf.y = y;
   340             nf.pressure = pressure;
   341             nf.xdelta = 0;
   342             nf.ydelta = 0;
   343             nf.last_x = x;
   344             nf.last_y = y;
   345             nf.last_pressure = pressure;
   346             nf.down = SDL_FALSE;
   347             if(SDL_AddFinger(touch,&nf) < 0) return 0;
   348             finger = SDL_GetFinger(touch,fingerid);
   349         }
   350         else if(finger->down) return 0;
   351         if(xin < touch->x_min || yin < touch->y_min) return 0; //should defer if only a partial input
   352         posted = 0;
   353         if (SDL_GetEventState(SDL_FINGERDOWN) == SDL_ENABLE) {
   354             SDL_Event event;
   355             event.tfinger.type = SDL_FINGERDOWN;
   356             event.tfinger.touchId = id;
   357             event.tfinger.x = x;
   358             event.tfinger.y = y;
   359             event.tfinger.dx = 0;
   360             event.tfinger.dy = 0;
   361             event.tfinger.pressure = pressure;
   362             event.tfinger.state = touch->buttonstate;
   363             event.tfinger.windowID = touch->focus ? touch->focus->id : 0;
   364             event.tfinger.fingerId = fingerid;
   365             posted = (SDL_PushEvent(&event) > 0);
   366         }
   367         if(posted) finger->down = SDL_TRUE;
   368         return posted;
   369     }
   370     else {
   371         if(finger == NULL) {
   372             SDL_SetError("Finger not found.");
   373             return 0;
   374         }      
   375         posted = 0;
   376         if (SDL_GetEventState(SDL_FINGERUP) == SDL_ENABLE) {
   377             SDL_Event event;
   378             event.tfinger.type = SDL_FINGERUP;
   379             event.tfinger.touchId =  id;
   380             event.tfinger.state = touch->buttonstate;
   381             event.tfinger.windowID = touch->focus ? touch->focus->id : 0;
   382             event.tfinger.fingerId = fingerid;
   383             //I don't trust the coordinates passed on fingerUp
   384             event.tfinger.x = finger->x; 
   385             event.tfinger.y = finger->y;
   386             event.tfinger.dx = 0;
   387             event.tfinger.dy = 0;
   388             event.tfinger.pressure = pressure;
   389 
   390             if(SDL_DelFinger(touch,fingerid) < 0) return 0;
   391             posted = (SDL_PushEvent(&event) > 0);
   392         }        
   393         return posted;
   394     }
   395 }
   396 
   397 int
   398 SDL_SendTouchMotion(SDL_TouchID id, SDL_FingerID fingerid, int relative, 
   399                     float xin, float yin, float pressurein)
   400 {
   401     SDL_Touch *touch;
   402     SDL_Finger *finger;
   403     int posted;
   404     Sint16 xrel, yrel;
   405     Uint16 x;
   406     Uint16 y;
   407     Uint16 pressure;
   408     
   409     touch = SDL_GetTouch(id);
   410     if (!touch) {
   411       return SDL_TouchNotFoundError(id);
   412     }
   413 
   414     //scale to Integer coordinates
   415     x = (Uint16)((xin+touch->x_min)*(touch->xres)/(touch->native_xres));
   416     y = (Uint16)((yin+touch->y_min)*(touch->yres)/(touch->native_yres));
   417     pressure = (Uint16)((pressurein+touch->pressure_min)*(touch->pressureres)/(touch->native_pressureres));
   418     if(touch->flush_motion) {
   419         return 0;
   420     }
   421     
   422     finger = SDL_GetFinger(touch,fingerid);
   423     if(finger == NULL || !finger->down) {
   424         return SDL_SendFingerDown(id,fingerid,SDL_TRUE,xin,yin,pressurein);        
   425     } else {
   426         /* the relative motion is calculated regarding the last position */
   427         if (relative) {
   428             xrel = x;
   429             yrel = y;
   430             x = (finger->last_x + x);
   431             y = (finger->last_y + y);
   432         } else {
   433             if(xin < touch->x_min) x = finger->last_x; /*If movement is only in one axis,*/
   434             if(yin < touch->y_min) y = finger->last_y; /*The other is marked as -1*/
   435             if(pressurein < touch->pressure_min) pressure = finger->last_pressure;
   436             xrel = x - finger->last_x;
   437             yrel = y - finger->last_y;
   438             //printf("xrel,yrel (%i,%i)\n",(int)xrel,(int)yrel);
   439         }
   440         
   441         /* Drop events that don't change state */
   442         if (!xrel && !yrel) {
   443 #if 0
   444             printf("Touch event didn't change state - dropped!\n");
   445 #endif
   446             return 0;
   447         }
   448         
   449         /* Update internal touch coordinates */
   450         
   451         finger->x = x;
   452         finger->y = y;
   453         
   454         /*Should scale to window? Normalize? Maintain Aspect?*/
   455         //SDL_GetWindowSize(touch->focus, &x_max, &y_max);
   456         
   457         /* make sure that the pointers find themselves inside the windows */
   458         /* only check if touch->xmax is set ! */
   459         /*
   460           if (x_max && touch->x > x_max) {
   461           touch->x = x_max;
   462           } else if (touch->x < 0) {
   463           touch->x = 0;
   464           }
   465           
   466           if (y_max && touch->y > y_max) {
   467           touch->y = y_max;
   468           } else if (touch->y < 0) {
   469           touch->y = 0;
   470           }
   471         */
   472         finger->xdelta = xrel;
   473         finger->ydelta = yrel;
   474         finger->pressure = pressure;
   475         
   476         
   477         
   478         /* Post the event, if desired */
   479         posted = 0;
   480         if (SDL_GetEventState(SDL_FINGERMOTION) == SDL_ENABLE) {
   481             SDL_Event event;
   482             event.tfinger.type = SDL_FINGERMOTION;
   483             event.tfinger.touchId = id;
   484             event.tfinger.fingerId = fingerid;
   485             event.tfinger.x = x;
   486             event.tfinger.y = y;
   487             event.tfinger.dx = xrel;
   488             event.tfinger.dy = yrel;            
   489                 
   490             event.tfinger.pressure = pressure;
   491             event.tfinger.state = touch->buttonstate;
   492             event.tfinger.windowID = touch->focus ? touch->focus->id : 0;
   493             posted = (SDL_PushEvent(&event) > 0);
   494         }
   495         finger->last_x = finger->x;
   496         finger->last_y = finger->y;
   497         finger->last_pressure = finger->pressure;
   498         return posted;
   499     }
   500 }
   501 
   502 int
   503 SDL_SendTouchButton(SDL_TouchID id, Uint8 state, Uint8 button)
   504 {
   505     SDL_Touch *touch;
   506     int posted;
   507     Uint32 type;
   508 
   509     
   510     touch = SDL_GetTouch(id);
   511     if (!touch) {
   512       return SDL_TouchNotFoundError(id);
   513     }
   514 
   515     /* Figure out which event to perform */
   516     switch (state) {
   517     case SDL_PRESSED:
   518         if (touch->buttonstate & SDL_BUTTON(button)) {
   519             /* Ignore this event, no state change */
   520             return 0;
   521         }
   522         type = SDL_TOUCHBUTTONDOWN;
   523         touch->buttonstate |= SDL_BUTTON(button);
   524         break;
   525     case SDL_RELEASED:
   526         if (!(touch->buttonstate & SDL_BUTTON(button))) {
   527             /* Ignore this event, no state change */
   528             return 0;
   529         }
   530         type = SDL_TOUCHBUTTONUP;
   531         touch->buttonstate &= ~SDL_BUTTON(button);
   532         break;
   533     default:
   534         /* Invalid state -- bail */
   535         return 0;
   536     }
   537 
   538     /* Post the event, if desired */
   539     posted = 0;
   540     if (SDL_GetEventState(type) == SDL_ENABLE) {
   541         SDL_Event event;
   542         event.type = type;
   543         event.tbutton.touchId = touch->id;
   544         event.tbutton.state = state;
   545         event.tbutton.button = button;
   546         event.tbutton.windowID = touch->focus ? touch->focus->id : 0;
   547         posted = (SDL_PushEvent(&event) > 0);
   548     }
   549     return posted;
   550 }
   551 
   552 char *
   553 SDL_GetTouchName(SDL_TouchID id)
   554 {
   555     SDL_Touch *touch = SDL_GetTouch(id);
   556     if (!touch) {
   557         return NULL;
   558     }
   559     return touch->name;
   560 }
   561 
   562 int SDL_TouchNotFoundError(SDL_TouchID id) {
   563   //int i;
   564   SDL_SetError("ERROR: Cannot send touch on non-existent device with id: %li make sure SDL_AddTouch has been called\n",id);
   565 #if 0
   566   printf("ERROR: There are %i touches installed with Id's:\n",SDL_num_touch);
   567   for(i=0;i < SDL_num_touch;i++) {
   568     printf("ERROR: %li\n",SDL_touchPads[i]->id);
   569   }
   570 #endif
   571   return 0;
   572 }
   573 /* vi: set ts=4 sw=4 expandtab: */