*/}}
Browse Source

joystick event interactive picking

YimingWu 3 months ago
parent
commit
2c536eca0d
5 changed files with 175 additions and 46 deletions
  1. 105 20
      la_controllers.c
  2. 24 5
      la_data.c
  3. 20 10
      la_interface.h
  4. 2 1
      la_kernel.c
  5. 24 10
      resources/la_operators.c

+ 105 - 20
la_controllers.c

@@ -60,6 +60,25 @@ laController* la_NewController(char* name, char* path, int device, int NumAxes,
     c->InternalType = la_IdentifyControllerInternalType(name);
     lstAppendItem(&MAIN.Controllers,c);
     c->UserAssignedID=MAIN.NextControllerID; MAIN.NextControllerID++;
+    
+    laPropContainer*pc=la_EnsureSubTarget(LA_PROP_CONTROLLER,c);
+    if(!pc){ return c; }
+    for(laProp* p=pc->Props.pFirst;p;p=p->Item.pNext){
+        if(p->PropertyType & LA_PROP_INT && p->Offset){ int off=p->Offset;
+            if(off >= offsetof(laController,AxisValues[0]) && off <= offsetof(laController,AxisValues[LA_JS_MAX_AXES-1])){
+                int index = (off - offsetof(laController,AxisValues[0]))/sizeof(u16bit);
+                if(p->Len<2){ strSafeSet(&c->AxisNames[index], p->Name); }
+                else for(int i=0;i<p->Len;i++){ strSafePrint(&c->AxisNames[index+i],"%s.%d", p->Name,i+1); }
+            }
+        }elif(p->PropertyType & LA_PROP_ENUM && p->Offset){ int off=p->Offset;
+            if(off >= offsetof(laController,ButtonValues[0]) && off <= offsetof(laController,ButtonValues[LA_JS_MAX_BUTTONS-1])){
+                int index = (off - offsetof(laController,ButtonValues[0]));
+                if(p->Len<2){ strSafeSet(&c->ButtonNames[index], p->Name); }
+                else for(int i=0;i<p->Len;i++){ strSafePrint(&c->ButtonNames[index+i],"%s.%d", p->Name,i+1); }
+            }
+        }
+    }
+
     return c;
 }
 void la_DestroyController(laController* c){
@@ -97,6 +116,22 @@ int la_ControllerAxisToMap(int abs){
 int la_ControllerAxisToIndex(laController* c,int abs){
     int map=la_ControllerAxisToMap(abs); if(map<0) return -1; return c->AxisMap[map];
 }
+char* laControllerIDGetAxisName(int id, int axis){
+    laController* c=la_FindControllerWithID(id); if(!c){ return "?"; }
+    return SSTR(c->AxisNames[axis]);
+}
+char* laControllerIDGetButtonName(int id, int button){
+    laController* c=la_FindControllerWithID(id); if(!c){ return "?"; }
+    return SSTR(c->ButtonNames[button]);
+}
+int laControllerIDGetAxis(int id, char* name){
+    laController* c=la_FindControllerWithID(id); if(!c){ return -1; }
+    for(int i=0;i<LA_JS_MAX_AXES;i++){ if(strSame(SSTR(c->AxisNames[i]),name)) return i; } return -1;
+}
+int laControllerIDGetButton(int id, char* name){
+    laController* c=la_FindControllerWithID(id); if(!c){ return -1; }
+    for(int i=0;i<LA_JS_MAX_AXES;i++){ if(strSame(SSTR(c->ButtonNames[i]),name)) return i; } return -1;
+}
 
 void la_InitControllers(){
 #ifdef __linux__
@@ -109,9 +144,7 @@ void la_InitControllers(){
 		sprintf(fileName, "/dev/input/event%d", i);
 		int file = open(fileName, O_RDWR | O_NONBLOCK);
 		if (file != -1){
-			// Get name
 			ioctl(file, EVIOCGNAME(128), name);
-            printf("%s\n",name);
 
             barray_t *abs_barray = barray_init(ABS_CNT);
             ioctl(file, EVIOCGBIT(EV_ABS, ABS_CNT), barray_data(abs_barray));
@@ -156,12 +189,14 @@ void la_UpdateControllerStatus(){
             if(event.type == EV_KEY){
                 int idx = la_ControllerButtonToIndex(c,event.code); if(idx<0) continue;
                 c->ButtonValues[idx]=event.value; HasEvent=1;
-                //printf("b %d\n", idx);
+                MAIN.LastControllerKey=idx; MAIN.LastControllerKeyDevice=c->UserAssignedID; MAIN.ControllerHasNewKey = 1; laRetriggerOperators();
             }
             else if(event.type == EV_ABS){
-                int idx = la_ControllerAxisToIndex(c,event.code); if(idx<0) continue;
+                int idx = la_ControllerAxisToIndex(c,event.code); if(idx<0) continue; HasEvent=1;
                 c->AxisValues[idx]=rint(tnsLinearItp(-32768.0f,32767.0f,((real)event.value/(c->AxisMaxes[idx]-c->AxisMins[idx]))));
-                HasEvent=1; //printf("a %d %d\n", idx, event.value);
+                if(abs(c->AxisValues[idx]-c->SaveAxisValues[idx])>10000){ c->SaveAxisValues[idx]=c->AxisValues[idx];
+                    MAIN.LastControllerAxis=idx; MAIN.LastControllerAxisDevice=c->UserAssignedID; MAIN.ControllerHasNewAxis = 1; laRetriggerOperators();
+                }
             }
         }
         if(bytes<=0){ struct stat buffer; if(stat(c->Path->Ptr,&buffer)<0){ c->Error=1; HasEvent=1; } }
@@ -195,16 +230,16 @@ void la_AddButtonProp(laPropContainer* pc, char* id, char* name, char* desc, int
     p->ElementBytes=1;
 }
 void la_AddAxisProp(laPropContainer* pc, char* id, char* name, char* id_min, char* id_max, char* id_cmin, char* id_cmax, char* desc, int i, int array_len, char* array_prefix){
-    laProp* p=laAddIntProperty(pc,id,name,desc,array_len>1?LA_WIDGET_VALUE_METER_2D:LA_WIDGET_METER_TYPE2,
-        array_prefix,0,32767,-32768,1,0,0,offsetof(laController, AxisValues[i]),0,0,array_len,0,0,0,0,0,0,0,LA_READ_ONLY);
-    if(id_min) p=laAddIntProperty(pc,id_min,id_min,desc,0,
-        array_prefix,0,-25000,-32768,1,0,0,offsetof(laController, AxisLimitMins[i]),0,0,array_len,0,0,0,0,0,0,0,0);
-    if(id_max) p=laAddIntProperty(pc,id_max,id_max,desc,0,
-        array_prefix,0,32767,25000,1,0,0,offsetof(laController, AxisLimitMaxes[i]),0,0,array_len,0,0,0,0,0,0,0,0);
-    if(id_min) p=laAddIntProperty(pc,id_cmax,id_cmax,desc,0,
-        array_prefix,0,-25000,-32768,1,0,0,offsetof(laController, AxisCenterMins[i]),0,0,array_len,0,0,0,0,0,0,0,0);
-    if(id_cmax) p=laAddIntProperty(pc,id_cmin,id_cmin,desc,0,
-        array_prefix,0,32767,25000,1,0,0,offsetof(laController, AxisCenterMaxes[i]),0,0,array_len,0,0,0,0,0,0,0,0);
+    laAddIntProperty(pc,id,name,desc,array_len>1?LA_WIDGET_VALUE_METER_2D:LA_WIDGET_METER_TYPE2,
+        array_prefix,0,32767,-32768,1,0,0,offsetof(laController, AxisValues[i]),0,0,array_len,0,0,0,0,0,0,0,LA_READ_ONLY)->ElementBytes = 2;
+    if(id_min) laAddIntProperty(pc,id_min,id_min,desc,0,
+        array_prefix,0,-25000,-32768,1,0,0,offsetof(laController, AxisLimitMins[i]),0,0,array_len,0,0,0,0,0,0,0,0)->ElementBytes = 2;
+    if(id_max) laAddIntProperty(pc,id_max,id_max,desc,0,
+        array_prefix,0,32767,25000,1,0,0,offsetof(laController, AxisLimitMaxes[i]),0,0,array_len,0,0,0,0,0,0,0,0)->ElementBytes = 2;
+    if(id_cmin) laAddIntProperty(pc,id_cmax,id_cmax,desc,0,
+        array_prefix,0,-25000,-32768,1,0,0,offsetof(laController, AxisCenterMins[i]),0,0,array_len,0,0,0,0,0,0,0,0)->ElementBytes = 2;
+    if(id_cmax) laAddIntProperty(pc,id_cmin,id_cmin,desc,0,
+        array_prefix,0,32767,25000,1,0,0,offsetof(laController, AxisCenterMaxes[i]),0,0,array_len,0,0,0,0,0,0,0,0)->ElementBytes = 2;
 }
 void la_AddGenericButtonProps(laPropContainer* pc){
     for(int i=0;i<LA_JS_MAX_BUTTONS;i++){ char* name=LA_JS_BTN_NAMES[i]; la_AddButtonProp(pc,name,name,name,i,0,0); }
@@ -215,6 +250,7 @@ void la_AddGenericAxisProps(laPropContainer* pc){
 #define ADD_AXIS_PROP(pc,id,name,desc,i,array_len,array_prefix) \
     la_AddAxisProp(pc,id,name,id "_min",id "_max",id "_cmin",id "_cmax",desc,i,array_len,array_prefix)
 
+void laui_GenericJoystick(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context);
 void laui_X56Throttle(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context);
 void laui_X56Stick(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context);
 
@@ -238,11 +274,11 @@ void la_RegisterControllerProps(){
 
     laPropContainer* pc; laProp* p;
 
-    pc=laAddPropertyContainer("la_controller", "Controller", "A joystick/gamepad controller", U'🕹', 0, sizeof(laController), 0,0,1);
+    pc=laAddPropertyContainer("la_controller", "Controller", "A joystick/gamepad controller", U'🕹', laui_GenericJoystick, sizeof(laController), 0,0,1);
     LA_PC_JS_GENERIC = pc;
-    laAddStringProperty(pc,"name","Name","Name of the controller", LA_WIDGET_STRING_PLAIN,0,0,0,1,offsetof(laController,Name),0,0,0,0,LA_READ_ONLY);
+    laAddStringProperty(pc,"name","Name","Name of the controller", LA_WIDGET_STRING_PLAIN,0,0,0,1,offsetof(laController,Name),0,0,0,0,LA_READ_ONLY|LA_AS_IDENTIFIER);
     laAddStringProperty(pc,"path","Path","Device path to the controller", LA_WIDGET_STRING_PLAIN,0,0,0,1,offsetof(laController,Path),0,0,0,0,LA_READ_ONLY);
-    laAddIntProperty(pc,"user_assigned_id", "ID", "User assigned ID", 0,0,0,0,0,0,0,0,offsetof(laController, UserAssignedID),0,0,0,0,0,0,0,0,0,0,LA_AS_IDENTIFIER);
+    laAddIntProperty(pc,"user_assigned_id", "ID", "User assigned ID", 0,0,0,0,0,0,0,0,offsetof(laController, UserAssignedID),0,0,0,0,0,0,0,0,0,0,0);
     laAddIntProperty(pc,"error", "Error", "Device error", 0,0,0,0,0,0,0,0,offsetof(laController, Error),0,0,0,0,0,0,0,0,0,0,0);
     la_AddGenericButtonProps(pc);
     la_AddGenericAxisProps(pc);
@@ -307,6 +343,55 @@ void la_RegisterControllerProps(){
     //button 14-16 not sure where it is....
 }
 
+void laui_GenericJoystick(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
+    laColumn* c=laFirstColumn(uil); laUiItem* b,*b1;
+    char buf[32];
+
+    laShowItem(uil,c,This,"name")->Flags|=LA_TEXT_ALIGN_CENTER;
+    b=laBeginRow(uil,c,0,0);
+    laShowItem(uil,c,This,"user_assigned_id");
+    b1=laOnConditionThat(uil,c,laPropExpression(This,"error"));{
+        laShowLabel(uil,c,"Device error.",0,0)->Expand=1;
+    }laElse(uil,b1);{
+        laShowItem(uil,c,This,"path")->Expand=1;
+    }laEndCondition(uil,b1);
+    laEndRow(uil,b);
+    laShowSeparator(uil,c);
+
+    laShowLabel(uil,c,"Axes",0,0);
+    for(int i=0;i<8;i++){
+        if(!(i%2)){ b=laBeginRow(uil,c,0,0); }
+        sprintf(buf,"a%d",i);
+        laShowLabel(uil,c,buf,0,0); laShowItem(uil,c,This,buf)->Expand=1;
+        if(i%2){ laEndRow(uil,b); }
+    }
+    b1=laOnConditionToggle(uil,c,0,0,0,0,0); strSafeSet(&b1->ExtraInstructions,"text=More axes..."); b1->Flags|=LA_TEXT_ALIGN_LEFT;
+    for(int i=8;i<LA_JS_MAX_AXES;i++){
+        if(!(i%2)){ b=laBeginRow(uil,c,0,0); }
+        sprintf(buf,"a%d",i);
+        laShowLabel(uil,c,buf,0,0); laShowItem(uil,c,This,buf)->Expand=1;
+        if(i%2){ laEndRow(uil,b); }
+    }
+    laEndCondition(uil,b1);
+    
+    laShowSeparator(uil,c);
+
+    laShowLabel(uil,c,"Buttons",0,0);
+    for(int i=0;i<16;i++){
+        if(!(i%2)){ b=laBeginRow(uil,c,0,0); }
+        sprintf(buf,"b%d",i);
+        laShowLabel(uil,c,buf,0,0); laShowItem(uil,c,This,buf)->Expand=1;
+        if(i%2){ laEndRow(uil,b); }
+    }
+    b1=laOnConditionToggle(uil,c,0,0,0,0,0); strSafeSet(&b1->ExtraInstructions,"text=More buttons..."); b1->Flags|=LA_TEXT_ALIGN_LEFT;
+    for(int i=8;i<LA_JS_MAX_BUTTONS;i++){
+        if(!(i%2)){ b=laBeginRow(uil,c,0,0); }
+        sprintf(buf,"b%d",i);
+        laShowLabel(uil,c,buf,0,0); laShowItem(uil,c,This,buf)->Expand=1;
+        if(i%2){ laEndRow(uil,b); }
+    }
+    laEndCondition(uil,b1);
+}
 
 void laui_X56Throttle(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil),*cl, *cr, *crl,*crr, *rc, *vc, *rcl,*rcr;
@@ -345,9 +430,9 @@ void laui_X56Throttle(laUiList *uil, laPropPack *This, laPropPack *Extra, laColu
     laShowCompoundValue(ui,LA_SLOT_MARKER_1,This,"thr2_min");
     laShowCompoundValue(ui,LA_SLOT_MARKER_2,This,"thr2_max");
     laEndRow(uil,b);
-    b=laBeginRow(uil,crl,0,0);
+    b=laBeginRow(uil,crl,1,1);
     laShowItem(uil,crl,This,"thr1_min")->Flags|=LA_UI_FLAGS_KNOB; laShowItem(uil,crl,This,"thr1_max")->Flags|=LA_UI_FLAGS_KNOB;
-    laShowSeparator(uil,crl)->Expand=1;
+    //laShowSeparator(uil,crl)->Expand=1;
     laShowItem(uil,crl,This,"thr2_min")->Flags|=LA_UI_FLAGS_KNOB; laShowItem(uil,crl,This,"thr2_max")->Flags|=LA_UI_FLAGS_KNOB;
     laEndRow(uil,b);
 

+ 24 - 5
la_data.c

@@ -1355,7 +1355,12 @@ int laSetIntArraySingle(laPropPack *pp, int index, int n){
             return 1;
         }elif (pp->LastPs->p->Offset>=0){
             int *src = (pp->LastPs->p->OffsetIsPointer) ? ((int **)((BYTE *)pp->LastPs->UseInstance + pp->LastPs->p->Offset)) : ((int *)((BYTE *)pp->LastPs->UseInstance + pp->LastPs->p->Offset));
-            src[index] = n; laNotifyUsersPP(pp); return 1;
+            switch(pp->LastPs->p->ElementBytes){
+                default: case 4: src[index] = n; break;
+                case 2: ((short*)src)[index]=n; break;
+                case 1: ((char*)src)[index]=n; break;
+            }
+            laNotifyUsersPP(pp); return 1;
         }
     }
     return 0;
@@ -1382,9 +1387,15 @@ int laSetIntArrayAll(laPropPack *pp, int n){
                 p->SetArr(pp->LastPs->UseInstance, i, n);
             }
         }elif (pp->LastPs->p->Offset>=0){
-            real *src = (pp->LastPs->p->OffsetIsPointer) ? ((real **)((BYTE *)pp->LastPs->UseInstance + pp->LastPs->p->Offset)) : ((real *)((BYTE *)pp->LastPs->UseInstance + pp->LastPs->p->Offset));
+            int *src = (pp->LastPs->p->OffsetIsPointer) ? ((int **)((BYTE *)pp->LastPs->UseInstance + pp->LastPs->p->Offset)) : ((int *)((BYTE *)pp->LastPs->UseInstance + pp->LastPs->p->Offset));
             int i = 0, len = laGetArrayLength(pp);
-            for (i; i < len; i++){ src[i] = n; }
+            for (i; i < len; i++){ 
+                switch(pp->LastPs->p->ElementBytes){
+                    default: case 4: src[i] = n; break;
+                    case 2: ((short*)src)[i]=n; break;
+                    case 1: ((char*)src)[i]=n; break;
+                }
+            }
         }
         laNotifyUsersPP(pp);
         return 1;
@@ -1430,7 +1441,11 @@ int laSetIntArrayAllArray(laPropPack *pp, int *arr){
             int *src = (pp->LastPs->p->OffsetIsPointer) ? ((int **)((BYTE *)pp->LastPs->UseInstance + pp->LastPs->p->Offset)) : ((int *)((BYTE *)pp->LastPs->UseInstance + pp->LastPs->p->Offset));
             int i = 0, len = laGetArrayLength(pp);
             for (i; i < len; i++){
-                src[i] = arr[i];
+                switch(pp->LastPs->p->ElementBytes){
+                    default: case 4: src[i] =arr[i]; break;
+                    case 2: ((short*)src)[i]=arr[i]; break;
+                    case 1: ((char*)src)[i]=arr[i]; break;
+                }
             }
         }
         laNotifyUsersPP(pp);
@@ -1452,7 +1467,11 @@ int laGetIntArray(laPropPack *pp, int *result){
             if(len==1){ *result=laGetInt(pp); return 1; }
             if(pp->LastPs->p->Offset>=0){
                 int *src = (pp->LastPs->p->OffsetIsPointer) ? ((int **)((BYTE *)pp->LastPs->UseInstance + pp->LastPs->p->Offset)) : ((int *)((BYTE *)pp->LastPs->UseInstance + pp->LastPs->p->Offset));
-                memcpy(result, src, len * sizeof(int));
+                switch(pp->LastPs->p->ElementBytes){
+                    default: case 4: memcpy(result, src, len * sizeof(int)); break;
+                    case 2: for(int i=0;i<len;i++){ result[i]=((short*)src)[i]; }
+                    case 1: for(int i=0;i<len;i++){ result[i]=((char*)src)[i]; }
+                }
             }else{ *result=0; }
             return 1;
         }else{

+ 20 - 10
la_interface.h

@@ -320,6 +320,8 @@ STRUCTURE(LA){
 #endif
 
     laListHandle Controllers; int NextControllerID;
+    u8bit LastControllerKey, LastControllerKeyDevice, ControllerHasNewKey;
+    u8bit LastControllerAxis, LastControllerAxisDevice, ControllerHasNewAxis;
 
     laListHandle Windows;
     laListHandle WastedPanels;
@@ -1436,7 +1438,7 @@ STRUCTURE(laInputMappingEntry){
     laListItem Item;
     laInputMapping* Parent;
     int DeviceType;
-    int JoystickDevice;
+    int JoystickDevice; int Axis, Button;
     laSafeString* Key; int KeyValue;
     int SpecialKeyBits;
     laSafeString* Signal; int SignalValue;
@@ -1458,7 +1460,7 @@ STRUCTURE(laCustomSignal){
     int Signal;
 };
 
-#define LA_JS_MAX_AXES 32
+#define LA_JS_MAX_AXES 64
 #define LA_JS_MAX_BUTTONS 128
 STRUCTURE(laController){
     laListItem Item;
@@ -1467,16 +1469,19 @@ STRUCTURE(laController){
     int fd; //device;
     int NumButtons; int NumAxes; int UserAssignedID; int Error;
     int InternalType; // used to identify models and use specific props.
-    int AxisValues[LA_JS_MAX_AXES];
-    int AxisMaxes[LA_JS_MAX_AXES]; // original max scaled to 32767;
+    short AxisValues[LA_JS_MAX_AXES],SaveAxisValues[LA_JS_MAX_AXES];
+    int AxisMaxes[LA_JS_MAX_AXES];
     int AxisMins[LA_JS_MAX_AXES];
-    int AxisLimitMaxes[LA_JS_MAX_AXES]; // calibrated max;
-    int AxisLimitMins[LA_JS_MAX_AXES];
-    int AxisCenterMaxes[LA_JS_MAX_AXES]; // calibrated center;
-    int AxisCenterMins[LA_JS_MAX_AXES];
+    short AxisLimitMaxes[LA_JS_MAX_AXES]; // calibrated max;
+    short AxisLimitMins[LA_JS_MAX_AXES];
+    short AxisCenterMaxes[LA_JS_MAX_AXES]; // calibrated center;
+    short AxisCenterMins[LA_JS_MAX_AXES];
     char ButtonValues[LA_JS_MAX_BUTTONS];
-    u8bit ButtonsMap[128];
-    u8bit AxisMap[64];
+
+    u8bit ButtonsMap[LA_JS_MAX_BUTTONS];
+    u8bit AxisMap[LA_JS_MAX_AXES];
+    laSafeString* ButtonNames[LA_JS_MAX_BUTTONS];
+    laSafeString* AxisNames[LA_JS_MAX_AXES];
 };
 
 void la_InitControllers();
@@ -1485,6 +1490,11 @@ void la_RegisterControllerProps();
 laPropContainer* laget_ControllerType(laController* c);
 laController* la_FindControllerWithID(int id);
 
+char* laControllerIDGetAxisName(int id, int axis);
+char* laControllerIDGetButtonName(int id, int button);
+int laControllerIDGetAxis(int id, char* name);
+int laControllerIDGetButton(int id, char* name);
+
 void la_RegisterBasicNodes();
 
 #define LA_DAG_FLAG_ERR  0

+ 2 - 1
la_kernel.c

@@ -1228,8 +1228,8 @@ int laGetReadyWith(laInitArguments* ia){
     laFinalizeUiTemplates();
     laFinalizeOperators();
 
-    la_InitControllers();
     la_RegisterControllerProps();
+    la_InitControllers();
     
     la_RegisterBasicNodes();
     tns_RegisterNodes();
@@ -7457,6 +7457,7 @@ int la_HandleEvents(laWindow *w){
 
             memFree(e);
         }
+        MAIN.ControllerHasNewAxis = MAIN.ControllerHasNewKey = 0;
         if (!MAIN.ReTriggerOperators) break;
     }
     la_UpdateOperatorHints(w);

+ 24 - 10
resources/la_operators.c

@@ -794,7 +794,9 @@ void laui_InputMappingEntrySignalSelector(laUiList *uil, laPropPack *Base, laPro
 
 STRUCTURE(laKeyDetectorData){
     int pad;
+    int IsController;
     int Key;
+    int Button, Axis, Device;
     laSafeString* Str;
 };
 int OPEXT_InputMappingEntrySelectKey(laOperator* a,int exitmode){
@@ -805,7 +807,7 @@ int OPEXT_InputMappingEntrySelectKey(laOperator* a,int exitmode){
 int OPINV_InputMappingEntrySelectKey(laOperator *a, laEvent *e){
     if(!a->This || !a->This->EndInstance) return LA_CANCELED; laInputMappingEntry* ime=a->This->EndInstance;
     a->CustomData = memAcquire(sizeof(laKeyDetectorData));
-    laEnableOperatorPanel(a,a->This,e->x-LA_RH*5,e->y-LA_RH,LA_RH*10,LA_RH*10,LA_RH*20,LA_RH*20,0,0,0,0,0,0,e);
+    laEnableOperatorPanel(a,a->This,e->x-LA_RH*5,e->y-LA_RH,LA_RH*10,LA_RH*10,LA_RH*20,LA_RH*20,LA_RH*20,0,0,0,0,0,e);
     laOperatorModalOver(a);
     return LA_RUNNING;
 }
@@ -813,18 +815,25 @@ int OPMOD_InputMappingEntrySelectKey(laOperator *a, laEvent *e){
     if(!a->This || !a->This->EndInstance) return LA_CANCELED; laInputMappingEntry* ime=a->This->EndInstance;
     laKeyDetectorData* kdd=a->CustomData; if(!kdd){ return LA_FINISHED; }
     char buf[64],*_next=buf;
-    if(ime->DeviceType == LA_INPUT_DEVICE_KEYBOARD){
-        if(e->Type == LA_KEY_DOWN || e->Type == LA_KEY_UP){
-            kdd->Key = e->key; laToUTF8(e->key,buf,&_next); *_next=0; strSafeSet(&kdd->Str,buf);
-            laNotifyInstanceUsers(kdd);
-            return LA_RUNNING;
+    if(e->Type == LA_KEY_DOWN || e->Type == LA_KEY_UP){
+        kdd->Key = e->key; laToUTF8(e->key,buf,&_next); *_next=0; strSafeSet(&kdd->Str,buf); kdd->IsController=0;
+        laNotifyInstanceUsers(kdd);
+        return LA_RUNNING;
+    }elif(e->Type == LA_EMPTY){
+        if(MAIN.ControllerHasNewAxis){
+            kdd->Axis = MAIN.LastControllerAxis; kdd->Device = MAIN.LastControllerAxisDevice; kdd->Button =-1; kdd->IsController=1;
+            strSafeSet(&kdd->Str,laControllerIDGetAxisName(kdd->Device,kdd->Axis)); laNotifyInstanceUsers(kdd);
+        }elif(MAIN.ControllerHasNewKey){
+            kdd->Button = MAIN.LastControllerKey; kdd->Device = MAIN.LastControllerKeyDevice; kdd->Axis = -1; kdd->IsController=1;
+            strSafeSet(&kdd->Str,laControllerIDGetButtonName(kdd->Device,kdd->Button)); laNotifyInstanceUsers(kdd);
         }
-    }else{
-        return LA_FINISHED;
     }
     if(a->ConfirmData){
         if(a->ConfirmData->Mode == LA_CONFIRM_OK){
-            strSafeSet(&ime->Key,SSTR(kdd->Str)); ime->KeyValue = kdd->Key; laNotifyInstanceUsers(ime);
+            strSafeSet(&ime->Key,SSTR(kdd->Str));
+            if(kdd->IsController){ ime->DeviceType = LA_INPUT_DEVICE_JOYSTICK; ime->Axis=kdd->Axis; ime->Button=kdd->Button; ime->JoystickDevice = kdd->Device; }
+            else{ ime->DeviceType = LA_INPUT_DEVICE_KEYBOARD; ime->KeyValue = kdd->Key; }
+            laNotifyInstanceUsers(ime);
         }
         return LA_FINISHED_PASS;
     }
@@ -842,7 +851,12 @@ void laui_InputMappingEntryKeySelector(laUiList *uil, laPropPack *Base, laPropPa
     laEndRow(uil,b);
 }
 void laget_KeyDetectorPressedString(laKeyDetectorData* kdd, char* out, char** pivot){
-    sprintf(out, "\"%s\" (0x%0x)", SSTR(kdd->Str), kdd->Key);
+    if(kdd->IsController){
+        if(kdd->Axis>=0){ sprintf(out, "Controller %d axis \"%s\" (0x%0x)", kdd->Device, SSTR(kdd->Str), kdd->Axis); }
+        else{ sprintf(out, "Controller %d button \"%s\" (0x%0x)", kdd->Device, SSTR(kdd->Str), kdd->Button); }
+    }else{
+        sprintf(out, "Keyboard \"%s\" (0x%0x)", SSTR(kdd->Str), kdd->Key);
+    }
 }