diff --git a/applications/app-loader/app-loader.c b/applications/app-loader/app-loader.c
index c1b31339..df49b3e0 100644
--- a/applications/app-loader/app-loader.c
+++ b/applications/app-loader/app-loader.c
@@ -36,7 +36,7 @@ static void render_callback(Canvas* canvas, void* _ctx) {
 static void input_callback(InputEvent* input_event, void* _ctx) {
     AppLoaderState* ctx = (AppLoaderState*)_ctx;
 
-    if(input_event->state && input_event->input == InputBack) {
+    if(input_event->type == InputTypeShort && input_event->key == InputKeyBack) {
         osThreadTerminate(ctx->app_thread_id);
         view_port_enabled_set(ctx->view_port, false);
         api_hal_timebase_insomnia_exit();
diff --git a/applications/cc1101-workaround/cc1101-workaround.cpp b/applications/cc1101-workaround/cc1101-workaround.cpp
index 30e90507..2dfe86ff 100644
--- a/applications/cc1101-workaround/cc1101-workaround.cpp
+++ b/applications/cc1101-workaround/cc1101-workaround.cpp
@@ -237,8 +237,11 @@ const FreqConfig FREQ_LIST[] = {
     {&bands[10], 0},
 };
 
-extern "C" void cc1101_isr() {
-    gpio_write((GpioPin*)&debug_0, gpio_read(&cc1101_g0_gpio));
+extern "C" void cc1101_isr(void* _pin, void* _ctx) {
+    uint32_t pin = (uint32_t)_pin;
+    if(pin == CC1101_G0_Pin) {
+        gpio_write((GpioPin*)&debug_0, gpio_read(&cc1101_g0_gpio));
+    }
 }
 
 typedef enum {
@@ -343,7 +346,7 @@ static void input_callback(InputEvent* input_event, void* ctx) {
 }
 
 extern "C" void cc1101_workaround(void* p) {
-    osMessageQueueId_t event_queue = osMessageQueueNew(1, sizeof(AppEvent), NULL);
+    osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(AppEvent), NULL);
     furi_check(event_queue);
 
     State _state;
@@ -383,7 +386,7 @@ extern "C" void cc1101_workaround(void* p) {
     GpioPin cs_pin = {CC1101_CS_GPIO_Port, CC1101_CS_Pin};
 
     gpio_init(&cc1101_g0_gpio, GpioModeInput);
-
+    api_interrupt_add(cc1101_isr, InterruptTypeExternalInterrupt, NULL);
     // TODO open record
     GpioPin* cs_pin_record = &cs_pin;
     CC1101 cc1101(cs_pin_record);
@@ -425,7 +428,8 @@ extern "C" void cc1101_workaround(void* p) {
 
         if(event_status == osOK) {
             if(event.type == EventTypeKey) {
-                if(event.value.input.state && event.value.input.input == InputBack) {
+                if(event.value.input.type == InputTypeShort &&
+                   event.value.input.key == InputKeyBack) {
                     printf("[cc1101] bye!\r\n");
 
                     cc1101.SpiStrobe(CC1101_SIDLE);
@@ -438,7 +442,8 @@ extern "C" void cc1101_workaround(void* p) {
                     furiac_exit(NULL);
                 }
 
-                if(event.value.input.state && event.value.input.input == InputDown) {
+                if(event.value.input.type == InputTypeShort &&
+                   event.value.input.key == InputKeyDown) {
                     if(state->active_freq_idx > 0) {
                         state->active_freq_idx--;
                     }
@@ -448,7 +453,8 @@ extern "C" void cc1101_workaround(void* p) {
                     state->need_cc1101_conf = true;
                 }
 
-                if(event.value.input.state && event.value.input.input == InputUp) {
+                if(event.value.input.type == InputTypeShort &&
+                   event.value.input.key == InputKeyUp) {
                     if(state->active_freq_idx < (sizeof(FREQ_LIST) / sizeof(FREQ_LIST[0]) - 1)) {
                         state->active_freq_idx++;
                     }
@@ -458,7 +464,8 @@ extern "C" void cc1101_workaround(void* p) {
                     state->need_cc1101_conf = true;
                 }
 
-                if(event.value.input.state && event.value.input.input == InputRight) {
+                if(event.value.input.type == InputTypeShort &&
+                   event.value.input.key == InputKeyRight) {
                     /*
                     if(state->tx_level < (sizeof(TX_LEVELS) / sizeof(TX_LEVELS[0]) - 1)) {
                         state->tx_level++;
@@ -471,7 +478,8 @@ extern "C" void cc1101_workaround(void* p) {
                     state->need_cc1101_conf = true;
                 }
 
-                if(event.value.input.state && event.value.input.input == InputLeft) {
+                if(event.value.input.type == InputTypeShort &&
+                   event.value.input.key == InputKeyLeft) {
                     /*
                     if(state->tx_level < (sizeof(TX_LEVELS) / sizeof(TX_LEVELS[0]) - 1)) {
                         state->tx_level++;
@@ -484,9 +492,14 @@ extern "C" void cc1101_workaround(void* p) {
                     state->need_cc1101_conf = true;
                 }
 
-                if(event.value.input.input == InputOk) {
-                    state->mode = event.value.input.state ? ModeTx : ModeRx;
-                    state->need_cc1101_conf = true;
+                if(event.value.input.key == InputKeyOk) {
+                    if(event.value.input.type == InputTypePress) {
+                        state->mode = ModeTx;
+                        state->need_cc1101_conf = true;
+                    } else if(event.value.input.type == InputTypeRelease) {
+                        state->mode = ModeRx;
+                        state->need_cc1101_conf = true;
+                    }
                 }
             }
         } else {
diff --git a/applications/dolphin/dolphin.c b/applications/dolphin/dolphin.c
index 33e08ebf..ec52f931 100644
--- a/applications/dolphin/dolphin.c
+++ b/applications/dolphin/dolphin.c
@@ -4,13 +4,13 @@ bool dolphin_view_first_start_input(InputEvent* event, void* context) {
     furi_assert(event);
     furi_assert(context);
     Dolphin* dolphin = context;
-    if(event->state) {
-        if(event->input == InputLeft) {
+    if(event->type == InputTypeShort) {
+        if(event->key == InputKeyLeft) {
             with_view_model(
                 dolphin->idle_view_first_start, (DolphinViewFirstStartModel * model) {
                     if(model->page > 0) model->page--;
                 });
-        } else if(event->input == InputRight) {
+        } else if(event->key == InputKeyRight) {
             uint32_t page;
             with_view_model(
                 dolphin->idle_view_first_start,
@@ -30,13 +30,13 @@ bool dolphin_view_idle_main_input(InputEvent* event, void* context) {
     furi_assert(context);
     Dolphin* dolphin = context;
 
-    if(event->state) {
-        if(event->input == InputOk) {
+    if(event->type == InputTypeShort) {
+        if(event->key == InputKeyOk) {
             with_value_mutex(
                 dolphin->menu_vm, (Menu * menu) { menu_ok(menu); });
-        } else if(event->input == InputUp) {
+        } else if(event->key == InputKeyUp) {
             view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleStats);
-        } else if(event->input == InputDown) {
+        } else if(event->key == InputKeyDown) {
             view_dispatcher_switch_to_view(dolphin->idle_view_dispatcher, DolphinViewIdleDebug);
         }
     }
@@ -49,13 +49,13 @@ bool dolphin_view_idle_stats_input(InputEvent* event, void* context) {
     furi_assert(context);
     Dolphin* dolphin = context;
 
-    if(!event->state) return false;
+    if(event->type != InputTypeShort) return false;
 
-    if(event->input == InputLeft) {
+    if(event->key == InputKeyLeft) {
         dolphin_deed(dolphin, DolphinDeedWrong);
-    } else if(event->input == InputRight) {
+    } else if(event->key == InputKeyRight) {
         dolphin_deed(dolphin, DolphinDeedIButtonRead);
-    } else if(event->input == InputOk) {
+    } else if(event->key == InputKeyOk) {
         dolphin_save(dolphin);
     } else {
         return false;
diff --git a/applications/examples/input_dump.c b/applications/examples/input_dump.c
index 80645171..2284e90e 100644
--- a/applications/examples/input_dump.c
+++ b/applications/examples/input_dump.c
@@ -4,27 +4,17 @@
 
 typedef union {
     unsigned int packed;
-    InputState state;
+    InputType state;
 } InputDump;
 
-static void state_cb(const void* value, void* ctx) {
-    InputDump dump = {.packed = 0};
-    dump.state = *(InputState*)value;
-
-    printf("state: %02x\r\n", dump.packed);
-}
-
 static void event_cb(const void* value, void* ctx) {
     const InputEvent* event = value;
 
-    printf("event: %02x %s\r\n", event->input, event->state ? "pressed" : "released");
+    printf("event: %02x %s\r\n", event->key, event->type ? "pressed" : "released");
 }
 
 void application_input_dump(void* p) {
     // open record
-    ValueManager* state_record = furi_record_open("input_state");
-    subscribe_pubsub(&state_record->pubsub, state_cb, NULL);
-
     PubSub* event_record = furi_record_open("input_events");
     subscribe_pubsub(event_record, event_cb, NULL);
 
diff --git a/applications/examples/strobe.c b/applications/examples/strobe.c
index ea922196..8e837948 100644
--- a/applications/examples/strobe.c
+++ b/applications/examples/strobe.c
@@ -7,11 +7,11 @@ static void event_cb(const void* value, void* ctx) {
     uint32_t* delay_time = acquire_mutex(ctx, 0);
     if(delay_time == NULL) return;
 
-    if(event->input == InputUp && *delay_time < 1000) {
+    if(event->key == InputKeyUp && *delay_time < 1000) {
         *delay_time += 5;
     }
 
-    if(event->input == InputDown && *delay_time > 10) {
+    if(event->key == InputKeyDown && *delay_time > 10) {
         *delay_time -= 5;
     }
     release_mutex(ctx, delay_time);
diff --git a/applications/examples/vibro.c b/applications/examples/vibro.c
index 4189910d..fa4fdc2e 100644
--- a/applications/examples/vibro.c
+++ b/applications/examples/vibro.c
@@ -10,9 +10,14 @@ static void button_handler(const void* value, void* _ctx) {
     const InputEvent* event = value;
     Ctx* ctx = (Ctx*)_ctx;
 
-    if(event->input == InputOk) {
-        gpio_write(ctx->vibro, event->state);
-        gpio_write(ctx->led, !event->state);
+    if(event->key != InputKeyOk) return;
+
+    if(event->type == InputTypePress) {
+        gpio_write(ctx->led, false);
+        gpio_write(ctx->vibro, true);
+    } else if(event->type == InputTypeRelease) {
+        gpio_write(ctx->led, true);
+        gpio_write(ctx->vibro, false);
     }
 }
 
diff --git a/applications/floopper-bloopper b/applications/floopper-bloopper
index 318b5f6c..598edd54 160000
--- a/applications/floopper-bloopper
+++ b/applications/floopper-bloopper
@@ -1 +1 @@
-Subproject commit 318b5f6c43b0219ec490a088ef51421fc8b94ac5
+Subproject commit 598edd54197d58b35d8cc2513549ddda3938fa38
diff --git a/applications/gpio-tester/gpio-tester.c b/applications/gpio-tester/gpio-tester.c
index 92b6bd91..76575868 100644
--- a/applications/gpio-tester/gpio-tester.c
+++ b/applications/gpio-tester/gpio-tester.c
@@ -58,7 +58,7 @@ static void input_callback(InputEvent* input_event, void* ctx) {
 }
 
 void app_gpio_test(void* p) {
-    osMessageQueueId_t event_queue = osMessageQueueNew(1, sizeof(AppEvent), NULL);
+    osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(AppEvent), NULL);
     furi_check(event_queue);
 
     State _state;
@@ -88,34 +88,41 @@ void app_gpio_test(void* p) {
 
     AppEvent event;
     while(1) {
-        osStatus_t event_status = osMessageQueueGet(event_queue, &event, NULL, 150);
+        osStatus_t event_status = osMessageQueueGet(event_queue, &event, NULL, osWaitForever);
         State* state = (State*)acquire_mutex_block(&state_mutex);
 
         if(event_status == osOK) {
             if(event.type == EventTypeKey) {
-                if(event.value.input.state && event.value.input.input == InputBack) {
+                if(event.value.input.type == InputTypeShort &&
+                   event.value.input.key == InputKeyBack) {
                     printf("[gpio-tester] bye!\r\n");
                     // TODO remove all view_ports create by app
                     view_port_enabled_set(view_port, false);
                     furiac_exit(NULL);
                 }
 
-                if(event.value.input.state && event.value.input.input == InputRight) {
+                if(event.value.input.type == InputTypeShort &&
+                   event.value.input.key == InputKeyRight) {
                     if(state->gpio_index < (sizeof(GPIO_PINS) / sizeof(GPIO_PINS[0]) - 1)) {
                         state->gpio_index++;
                     }
                 }
 
-                if(event.value.input.state && event.value.input.input == InputLeft) {
+                if(event.value.input.type == InputTypeShort &&
+                   event.value.input.key == InputKeyLeft) {
                     if(state->gpio_index > 0) {
                         state->gpio_index--;
                     }
                 }
 
-                if(event.value.input.input == InputOk) {
-                    gpio_write(
-                        (GpioPin*)&GPIO_PINS[state->gpio_index].pin, event.value.input.state);
-                    gpio_write((GpioPin*)&led_gpio[1], !event.value.input.state);
+                if(event.value.input.key == InputKeyOk) {
+                    if(event.value.input.type == InputTypePress) {
+                        gpio_write((GpioPin*)&GPIO_PINS[state->gpio_index].pin, true);
+                        gpio_write((GpioPin*)&led_gpio[1], false);
+                    } else if(event.value.input.type == InputTypeRelease) {
+                        gpio_write((GpioPin*)&GPIO_PINS[state->gpio_index].pin, false);
+                        gpio_write((GpioPin*)&led_gpio[1], true);
+                    }
                 }
             }
         }
diff --git a/applications/gui/modules/dialog.c b/applications/gui/modules/dialog.c
index 74298e95..df20ad9a 100644
--- a/applications/gui/modules/dialog.c
+++ b/applications/gui/modules/dialog.c
@@ -43,11 +43,11 @@ static bool dialog_view_input_callback(InputEvent* event, void* context) {
     bool consumed = false;
 
     // Process key presses only
-    if(event->state && dialog->callback) {
-        if(event->input == InputLeft) {
+    if(event->type == InputTypeShort && dialog->callback) {
+        if(event->key == InputKeyLeft) {
             dialog->callback(DialogResultLeft, dialog->context);
             consumed = true;
-        } else if(event->input == InputRight) {
+        } else if(event->key == InputKeyRight) {
             dialog->callback(DialogResultRight, dialog->context);
             consumed = true;
         }
diff --git a/applications/gui/modules/dialog_ex.c b/applications/gui/modules/dialog_ex.c
index 23534b4f..4fbbc4f4 100644
--- a/applications/gui/modules/dialog_ex.c
+++ b/applications/gui/modules/dialog_ex.c
@@ -97,14 +97,14 @@ static bool dialog_ex_view_input_callback(InputEvent* event, void* context) {
         });
 
     // Process key presses only
-    if(event->state && dialog_ex->callback) {
-        if(event->input == InputLeft && left_text != NULL) {
+    if(event->type == InputTypeShort && dialog_ex->callback) {
+        if(event->key == InputKeyLeft && left_text != NULL) {
             dialog_ex->callback(DialogExResultLeft, dialog_ex->context);
             consumed = true;
-        } else if(event->input == InputOk && center_text != NULL) {
+        } else if(event->key == InputKeyOk && center_text != NULL) {
             dialog_ex->callback(DialogExResultCenter, dialog_ex->context);
             consumed = true;
-        } else if(event->input == InputRight && right_text != NULL) {
+        } else if(event->key == InputKeyRight && right_text != NULL) {
             dialog_ex->callback(DialogExResultRight, dialog_ex->context);
             consumed = true;
         }
diff --git a/applications/gui/modules/popup.c b/applications/gui/modules/popup.c
index 73a855d2..8f79850c 100644
--- a/applications/gui/modules/popup.c
+++ b/applications/gui/modules/popup.c
@@ -83,7 +83,7 @@ static bool popup_view_input_callback(InputEvent* event, void* context) {
     bool consumed = false;
 
     // Process key presses only
-    if(event->state && popup->callback) {
+    if(event->type == InputTypeShort && popup->callback) {
         popup->callback(popup->context);
         consumed = true;
     }
diff --git a/applications/gui/modules/submenu.c b/applications/gui/modules/submenu.c
index 02887fa1..d2873c8c 100644
--- a/applications/gui/modules/submenu.c
+++ b/applications/gui/modules/submenu.c
@@ -74,17 +74,17 @@ static bool submenu_view_input_callback(InputEvent* event, void* context) {
     furi_assert(submenu);
     bool consumed = false;
 
-    if(event->state) {
-        switch(event->input) {
-        case InputUp:
+    if(event->type == InputTypeShort) {
+        switch(event->key) {
+        case InputKeyUp:
             consumed = true;
             submenu_process_up(submenu);
             break;
-        case InputDown:
+        case InputKeyDown:
             consumed = true;
             submenu_process_down(submenu);
             break;
-        case InputOk:
+        case InputKeyOk:
             consumed = true;
             submenu_process_ok(submenu);
             break;
diff --git a/applications/gui/modules/text_input.c b/applications/gui/modules/text_input.c
index 80a7fe6b..88441b84 100644
--- a/applications/gui/modules/text_input.c
+++ b/applications/gui/modules/text_input.c
@@ -289,25 +289,25 @@ static bool text_input_view_input_callback(InputEvent* event, void* context) {
     furi_assert(text_input);
     bool consumed = false;
 
-    if(event->state) {
-        switch(event->input) {
-        case InputUp:
+    if(event->type == InputTypeShort) {
+        switch(event->key) {
+        case InputKeyUp:
             text_input_handle_up(text_input);
             consumed = true;
             break;
-        case InputDown:
+        case InputKeyDown:
             text_input_handle_down(text_input);
             consumed = true;
             break;
-        case InputLeft:
+        case InputKeyLeft:
             text_input_handle_left(text_input);
             consumed = true;
             break;
-        case InputRight:
+        case InputKeyRight:
             text_input_handle_right(text_input);
             consumed = true;
             break;
-        case InputOk:
+        case InputKeyOk:
             text_input_handle_ok(text_input);
             consumed = true;
             break;
diff --git a/applications/gui/view_dispatcher.c b/applications/gui/view_dispatcher.c
index 1ec57a4f..7cf9e422 100644
--- a/applications/gui/view_dispatcher.c
+++ b/applications/gui/view_dispatcher.c
@@ -89,11 +89,11 @@ void view_dispatcher_input_callback(InputEvent* event, void* context) {
     if(view_dispatcher->current_view) {
         is_consumed = view_input(view_dispatcher->current_view, event);
     }
-    if(!is_consumed && event->state) {
+    if(!is_consumed && event->type == InputTypeShort) {
         uint32_t view_id = VIEW_IGNORE;
-        if(event->input == InputBack) {
+        if(event->key == InputKeyBack) {
             view_id = view_previous(view_dispatcher->current_view);
-        } else if(event->input == InputOk) {
+        } else if(event->key == InputKeyOk) {
             view_id = view_next(view_dispatcher->current_view);
         }
         view_dispatcher_switch_to_view(view_dispatcher, view_id);
diff --git a/applications/ibutton/ibutton.cpp b/applications/ibutton/ibutton.cpp
index 06f76a38..8557d2d6 100644
--- a/applications/ibutton/ibutton.cpp
+++ b/applications/ibutton/ibutton.cpp
@@ -28,10 +28,11 @@ void AppiButton::run() {
 
     AppiButtonEvent event;
     while(1) {
-        if(get_event(&event, 20)) {
+        if(get_event(&event, 1024 / 8)) {
             if(event.type == AppiButtonEvent::EventTypeKey) {
                 // press events
-                if(event.value.input.state && event.value.input.input == InputBack) {
+                if(event.value.input.type == InputTypeShort &&
+                   event.value.input.key == InputKeyBack) {
                     view_port_enabled_set(view_port, false);
                     gui_remove_view_port(gui, view_port);
                     api_hal_timebase_insomnia_exit();
@@ -39,11 +40,13 @@ void AppiButton::run() {
                     osThreadExit();
                 }
 
-                if(event.value.input.state && event.value.input.input == InputLeft) {
+                if(event.value.input.type == InputTypeShort &&
+                   event.value.input.key == InputKeyLeft) {
                     decrease_mode();
                 }
 
-                if(event.value.input.state && event.value.input.input == InputRight) {
+                if(event.value.input.type == InputTypeShort &&
+                   event.value.input.key == InputKeyRight) {
                     increase_mode();
                 }
             }
diff --git a/applications/ibutton/ibutton_mode_cyfral_emulate.h b/applications/ibutton/ibutton_mode_cyfral_emulate.h
index 157e15ca..e0b2cb60 100644
--- a/applications/ibutton/ibutton_mode_cyfral_emulate.h
+++ b/applications/ibutton/ibutton_mode_cyfral_emulate.h
@@ -29,11 +29,11 @@ void AppiButtonModeCyfralEmulate::event(AppiButtonEvent* event, AppiButtonState*
         app->blink_green();
 
     } else if(event->type == AppiButtonEvent::EventTypeKey) {
-        if(event->value.input.state && event->value.input.input == InputUp) {
+        if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyUp) {
             app->decrease_cyfral_address();
         }
 
-        if(event->value.input.state && event->value.input.input == InputDown) {
+        if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyDown) {
             app->increase_cyfral_address();
         }
     }
diff --git a/applications/ibutton/ibutton_mode_cyfral_read.h b/applications/ibutton/ibutton_mode_cyfral_read.h
index a887289c..236c8e22 100644
--- a/applications/ibutton/ibutton_mode_cyfral_read.h
+++ b/applications/ibutton/ibutton_mode_cyfral_read.h
@@ -59,11 +59,11 @@ void AppiButtonModeCyfralRead::event(AppiButtonEvent* event, AppiButtonState* st
             }
         }
     } else if(event->type == AppiButtonEvent::EventTypeKey) {
-        if(event->value.input.state && event->value.input.input == InputUp) {
+        if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyUp) {
             app->decrease_cyfral_address();
         }
 
-        if(event->value.input.state && event->value.input.input == InputDown) {
+        if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyDown) {
             app->increase_cyfral_address();
         }
     }
diff --git a/applications/ibutton/ibutton_mode_dallas_emulate.h b/applications/ibutton/ibutton_mode_dallas_emulate.h
index 02a0f4dd..d502a578 100644
--- a/applications/ibutton/ibutton_mode_dallas_emulate.h
+++ b/applications/ibutton/ibutton_mode_dallas_emulate.h
@@ -48,11 +48,11 @@ void AppiButtonModeDallasEmulate::event(AppiButtonEvent* event, AppiButtonState*
             app->blink_green();
         }
     } else if(event->type == AppiButtonEvent::EventTypeKey) {
-        if(event->value.input.state && event->value.input.input == InputUp) {
+        if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyUp) {
             app->decrease_dallas_address();
         }
 
-        if(event->value.input.state && event->value.input.input == InputDown) {
+        if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyDown) {
             app->increase_dallas_address();
         }
     }
diff --git a/applications/ibutton/ibutton_mode_dallas_read.h b/applications/ibutton/ibutton_mode_dallas_read.h
index 2e23b38a..0f330342 100644
--- a/applications/ibutton/ibutton_mode_dallas_read.h
+++ b/applications/ibutton/ibutton_mode_dallas_read.h
@@ -46,11 +46,11 @@ void AppiButtonModeDallasRead::event(AppiButtonEvent* event, AppiButtonState* st
             }
         }
     } else if(event->type == AppiButtonEvent::EventTypeKey) {
-        if(event->value.input.state && event->value.input.input == InputUp) {
+        if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyUp) {
             app->decrease_dallas_address();
         }
 
-        if(event->value.input.state && event->value.input.input == InputDown) {
+        if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyDown) {
             app->increase_dallas_address();
         }
     }
diff --git a/applications/ibutton/ibutton_mode_dallas_write.h b/applications/ibutton/ibutton_mode_dallas_write.h
index 6f0f72c8..135511aa 100644
--- a/applications/ibutton/ibutton_mode_dallas_write.h
+++ b/applications/ibutton/ibutton_mode_dallas_write.h
@@ -39,11 +39,11 @@ void AppiButtonModeDallasWrite::event(AppiButtonEvent* event, AppiButtonState* s
         }
 
     } else if(event->type == AppiButtonEvent::EventTypeKey) {
-        if(event->value.input.state && event->value.input.input == InputUp) {
+        if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyUp) {
             app->decrease_dallas_address();
         }
 
-        if(event->value.input.state && event->value.input.input == InputDown) {
+        if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyDown) {
             app->increase_dallas_address();
         }
     }
diff --git a/applications/input/input.c b/applications/input/input.c
index 658412cc..92e8a0f4 100644
--- a/applications/input/input.c
+++ b/applications/input/input.c
@@ -1,131 +1,72 @@
-#include 
-#include 
-#include 
+#include "input_i.h"
 
-#ifdef APP_NFC
-void nfc_isr(void);
-#endif
+#define GPIO_Read(input_pin)                                                    \
+    (HAL_GPIO_ReadPin((GPIO_TypeDef*)input_pin.pin->port, input_pin.pin->pin) ^ \
+     input_pin.pin->inverted)
 
-#ifdef BUILD_CC1101
-void cc1101_isr();
-#endif
+static Input* input = NULL;
 
-static volatile bool initialized = false;
-static ValueManager input_state_record;
-static PubSub input_events_record;
-static Event event;
-static InputState input_state = {
-    false,
-};
+void input_press_timer_callback(void* arg) {
+    InputPin* input_pin = arg;
+    InputEvent event;
+    event.key = input_pin->key;
+    event.type = InputTypeLong;
+    notify_pubsub(&input->event_pubsub, &event);
+}
 
-static void exti_input_callback(void* _pin, void* _ctx);
+void input_isr(void* _pin, void* _ctx) {
+    osThreadFlagsSet(input->thread, INPUT_THREAD_FLAG_ISR);
+}
 
-void input_task(void* p) {
-    uint32_t state_bits = 0;
-    uint8_t debounce_counters[INPUT_COUNT];
+void input_task() {
+    input = furi_alloc(sizeof(Input));
+    input->thread = osThreadGetId();
+    init_pubsub(&input->event_pubsub);
+    furi_record_create("input_events", &input->event_pubsub);
 
-    if(!init_managed(&input_state_record, &input_state, sizeof(input_state))) {
-        printf("[input_task] cannot initialize ValueManager for input_state\r\n");
-        furiac_exit(NULL);
-    }
-    if(!init_pubsub(&input_events_record)) {
-        printf("[input_task] cannot initialize PubSub for input_events\r\n");
-        furiac_exit(NULL);
-    }
-    if(!init_event(&event)) {
-        printf("[input_task] cannot initialize Event\r\n");
-        furiac_exit(NULL);
+    const size_t pin_count = input_pins_count;
+    input->pin_states = furi_alloc(pin_count * sizeof(InputPinState));
+
+    api_interrupt_add(input_isr, InterruptTypeExternalInterrupt, NULL);
+
+    for(size_t i = 0; i < pin_count; i++) {
+        input->pin_states[i].pin = &input_pins[i];
+        input->pin_states[i].state = GPIO_Read(input->pin_states[i]);
+        input->pin_states[i].debounce = INPUT_DEBOUNCE_TICKS_HALF;
+        input->pin_states[i].press_timer =
+            osTimerNew(input_press_timer_callback, osTimerOnce, &input->pin_states[i], NULL);
     }
 
-    furi_record_create("input_state", &input_state_record);
-    furi_record_create("input_events", &input_events_record);
-
-    api_interrupt_add(exti_input_callback, InterruptTypeExternalInterrupt, NULL);
-
-    // we ready to work
-    initialized = true;
-
-    // Force state update
-    for(uint32_t i = 0; i < INPUT_COUNT; i++) {
-        debounce_counters[i] = DEBOUNCE_TICKS / 2;
-    }
-
-    for(;;) {
-        bool changed = false;
-        for(uint32_t i = 0; i < INPUT_COUNT; i++) {
-            bool input_state = false;
-
-            // dirty hack, f3 has no CHARGING pin
-            // TODO rewrite this
-            if(i < GPIO_INPUT_PINS_COUNT) {
-                input_state = gpio_read(&input_gpio[i]) ^ input_invert[i];
-            }
-
-            if(input_state) {
-                if(debounce_counters[i] < DEBOUNCE_TICKS) {
-                    debounce_counters[i] += 1;
-                    changed = true;
-                }
-            } else {
-                if(debounce_counters[i] > 0) {
-                    debounce_counters[i] -= 1;
-                    changed = true;
+    while(1) {
+        bool is_changing = false;
+        for(size_t i = 0; i < pin_count; i++) {
+            bool state = GPIO_Read(input->pin_states[i]);
+            if(input->pin_states[i].debounce > 0 &&
+               input->pin_states[i].debounce < INPUT_DEBOUNCE_TICKS) {
+                is_changing = true;
+                input->pin_states[i].debounce += (state ? 1 : -1);
+            } else if(input->pin_states[i].state != state) {
+                input->pin_states[i].state = state;
+                // Common state info
+                InputEvent event;
+                event.type = input->pin_states[i].state ? InputTypePress : InputTypeRelease;
+                event.key = input->pin_states[i].pin->key;
+                // Send Press/Release event
+                notify_pubsub(&input->event_pubsub, &event);
+                // Short/Long press logic
+                if(state) {
+                    osTimerStart(input->pin_states[i].press_timer, INPUT_LONG_PRESS_TICKS);
+                } else if(osTimerStop(input->pin_states[i].press_timer) == osOK) {
+                    event.type = InputTypeShort;
+                    notify_pubsub(&input->event_pubsub, &event);
                 }
             }
         }
 
-        if(!changed) {
-            uint32_t new_state_bits = 0;
-            for(uint32_t i = 0; i < INPUT_COUNT; i++) {
-                if(debounce_counters[i] == DEBOUNCE_TICKS) {
-                    new_state_bits |= (1 << i);
-                }
-            }
-            uint32_t changed_bits = new_state_bits ^ state_bits;
-
-            if(changed_bits != 0) {
-                // printf("[input] %02x -> %02x\n", state_bits, new_state_bits);
-                InputState new_state = _BITS2STATE(new_state_bits);
-                write_managed(&input_state_record, &new_state, sizeof(new_state), osWaitForever);
-
-                state_bits = new_state_bits;
-
-                for(uint32_t i = 0; i < INPUT_COUNT; i++) {
-                    if((changed_bits & (1 << i)) != 0) {
-                        bool state = (new_state_bits & (1 << i)) != 0;
-                        InputEvent event = {i, state};
-                        notify_pubsub(&input_events_record, &event);
-                    }
-                }
-            }
-
-            // Sleep: wait for event
-            wait_event(&event);
-        } else {
+        if(is_changing) {
             osDelay(1);
+        } else {
+            osThreadFlagsWait(INPUT_THREAD_FLAG_ISR, osFlagsWaitAny, osWaitForever);
         }
     }
 }
-
-static void exti_input_callback(void* _pin, void* _ctx) {
-    // interrupt manager get us pin constant, so...
-    uint32_t pin = (uint32_t)_pin;
-
-#ifdef APP_NFC
-    if(pin == NFC_IRQ_Pin) {
-        nfc_isr();
-        return;
-    }
-#endif
-
-#ifdef BUILD_CC1101
-    if(pin == CC1101_G0_Pin) {
-        cc1101_isr();
-        return;
-    }
-#endif
-
-    if(!initialized) return;
-
-    signal_event(&event);
-}
\ No newline at end of file
diff --git a/applications/input/input.h b/applications/input/input.h
index 8e555c41..a3500acb 100644
--- a/applications/input/input.h
+++ b/applications/input/input.h
@@ -1,40 +1,19 @@
-#ifndef __INPUT_H
-#define __INPUT_H
+#pragma once
 
-#include 
-
-#define INPUT_COUNT 7
+#include 
 
+/* Input Types
+ * Some of them are physical events and some logical
+ */
 typedef enum {
-    InputUp = 0,
-    InputDown,
-    InputRight,
-    InputLeft,
-    InputOk,
-    InputBack,
-    InputCharging,
-} Input;
+    InputTypePress, /* Press event, emitted after debounce */
+    InputTypeRelease, /* Release event, emitted after debounce */
+    InputTypeShort, /* Short event, emitted after InputTypeRelease done withing INPUT_LONG_PRESS interval */
+    InputTypeLong, /* Long event, emmited after INPUT_LONG_PRESS interval, asynchronouse to InputTypeRelease  */
+} InputType;
 
+/* Input Event, dispatches with PubSub */
 typedef struct {
-    Input input;
-    bool state;
+    InputKey key;
+    InputType type;
 } InputEvent;
-
-typedef struct {
-    bool up : 1;
-    bool down : 1;
-    bool right : 1;
-    bool left : 1;
-    bool ok : 1;
-    bool back : 1;
-    bool charging : 1;
-} __attribute__((packed)) InputState;
-
-#define _BITS2STATE(bits)                                                                        \
-    {                                                                                            \
-        .up = (((bits)&0x01) != 0), .down = (((bits)&0x02) != 0), .right = (((bits)&0x04) != 0), \
-        .left = (((bits)&0x08) != 0), .ok = (((bits)&0x10) != 0), .back = (((bits)&0x20) != 0),  \
-        .charging = (((bits)&0x40) != 0)                                                         \
-    }
-
-#endif /* __INPUT_H */
diff --git a/applications/input/input_i.h b/applications/input/input_i.h
new file mode 100644
index 00000000..2f0c883d
--- /dev/null
+++ b/applications/input/input_i.h
@@ -0,0 +1,34 @@
+#pragma once
+
+#include "input.h"
+
+#include 
+#include 
+#include 
+#include 
+
+#define INPUT_DEBOUNCE_TICKS_HALF (INPUT_DEBOUNCE_TICKS / 2)
+#define INPUT_LONG_PRESS_TICKS 2048
+#define INPUT_THREAD_FLAG_ISR 0x00000001
+
+/* Input pin state */
+typedef struct {
+    const InputPin* pin;
+    // State
+    volatile bool state;
+    volatile uint8_t debounce;
+    volatile osTimerId_t press_timer;
+} InputPinState;
+
+/* Input state */
+typedef struct {
+    osThreadId_t thread;
+    PubSub event_pubsub;
+    InputPinState* pin_states;
+} Input;
+
+/* Input press timer callback */
+void input_press_timer_callback(void* arg);
+
+/* Input interrupt handler */
+void input_isr(void* _pin, void* _ctx);
diff --git a/applications/irda/irda.c b/applications/irda/irda.c
index e43112cd..6da6f088 100644
--- a/applications/irda/irda.c
+++ b/applications/irda/irda.c
@@ -78,15 +78,15 @@ void render_carrier(Canvas* canvas, State* state) {
 }
 
 void input_carrier(AppEvent* event, State* state) {
-    if(event->value.input.input == InputOk) {
-        if(event->value.input.state) {
+    if(event->value.input.key == InputKeyOk) {
+        if(event->value.input.type == InputTypePress) {
             irda_pwm_set(duty_cycles[state->carrier_duty_cycle_id], state->carrier_freq);
-        } else {
+        } else if(event->value.input.type == InputTypeRelease) {
             irda_pwm_stop();
         }
     }
 
-    if(event->value.input.state && event->value.input.input == InputUp) {
+    if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyUp) {
         if(state->carrier_freq < 45000) {
             state->carrier_freq += 1000;
         } else {
@@ -94,7 +94,7 @@ void input_carrier(AppEvent* event, State* state) {
         }
     }
 
-    if(event->value.input.state && event->value.input.input == InputDown) {
+    if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyDown) {
         uint8_t duty_cycles_count = sizeof(duty_cycles) / sizeof(duty_cycles[0]);
         if(state->carrier_duty_cycle_id < (duty_cycles_count - 1)) {
             state->carrier_duty_cycle_id++;
@@ -137,8 +137,8 @@ void render_packet(Canvas* canvas, State* state) {
 }
 
 void input_packet(AppEvent* event, State* state) {
-    if(event->value.input.input == InputOk) {
-        if(event->value.input.state) {
+    if(event->value.input.key == InputKeyOk) {
+        if(event->value.input.type == InputTypeShort) {
             switch(state->packets[state->packet_id].protocol) {
             case IRDA_NEC:
                 ir_nec_send(
@@ -156,13 +156,13 @@ void input_packet(AppEvent* event, State* state) {
         }
     }
 
-    if(event->value.input.state && event->value.input.input == InputDown) {
+    if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyDown) {
         if(state->packet_id < (IRDA_PACKET_COUNT - 1)) {
             state->packet_id++;
         };
     }
 
-    if(event->value.input.state && event->value.input.input == InputUp) {
+    if(event->value.input.type == InputTypeShort && event->value.input.key == InputKeyUp) {
         if(state->packet_id > 0) {
             state->packet_id--;
         };
@@ -299,7 +299,8 @@ void irda(void* p) {
         if(event_status == osOK) {
             if(event.type == EventTypeKey) {
                 // press events
-                if(event.value.input.state && event.value.input.input == InputBack) {
+                if(event.value.input.type == InputTypeShort &&
+                   event.value.input.key == InputKeyBack) {
                     // remove all view_ports create by app
                     view_port_enabled_set(view_port, false);
                     gui_remove_view_port(gui, view_port);
@@ -311,13 +312,15 @@ void irda(void* p) {
                     furiac_exit(NULL);
                 }
 
-                if(event.value.input.state && event.value.input.input == InputLeft) {
+                if(event.value.input.type == InputTypeShort &&
+                   event.value.input.key == InputKeyLeft) {
                     if(state->mode_id > 0) {
                         state->mode_id--;
                     }
                 }
 
-                if(event.value.input.state && event.value.input.input == InputRight) {
+                if(event.value.input.type == InputTypeShort &&
+                   event.value.input.key == InputKeyRight) {
                     if(state->mode_id < (mode_count - 1)) {
                         state->mode_id++;
                     }
diff --git a/applications/lf-rfid/lf-rfid.c b/applications/lf-rfid/lf-rfid.c
index b1bb1691..382d5895 100644
--- a/applications/lf-rfid/lf-rfid.c
+++ b/applications/lf-rfid/lf-rfid.c
@@ -161,7 +161,7 @@ static void extract_data(uint8_t* buf, uint8_t* customer, uint32_t* em_data) {
 }
 
 void lf_rfid_workaround(void* p) {
-    osMessageQueueId_t event_queue = osMessageQueueNew(1, sizeof(AppEvent), NULL);
+    osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(AppEvent), NULL);
 
     // create pin
     GpioPin pull_pin = {.pin = RFID_PULL_Pin, .port = RFID_PULL_GPIO_Port};
@@ -222,7 +222,7 @@ void lf_rfid_workaround(void* p) {
     }
 
     while(1) {
-        osStatus_t event_status = osMessageQueueGet(event_queue, &event, NULL, 100);
+        osStatus_t event_status = osMessageQueueGet(event_queue, &event, NULL, 1024 / 8);
 
         if(event.type == EventTypeRx && event_status == osOK) {
             uint32_t dt = (event.value.rx.dwt_value - prev_dwt) / (SystemCoreClock / 1000000.0f);
@@ -285,7 +285,8 @@ void lf_rfid_workaround(void* p) {
             if(event_status == osOK) {
                 if(event.type == EventTypeKey) {
                     // press events
-                    if(event.value.input.state && event.value.input.input == InputBack) {
+                    if(event.value.input.type == InputTypePress &&
+                       event.value.input.key == InputKeyBack) {
                         hal_pwmn_stop(&TIM_C, TIM_CHANNEL_1); // TODO: move to furiac_onexit
                         gpio_init(pull_pin_record, GpioModeInput);
                         gpio_init((GpioPin*)&ibutton_gpio, GpioModeInput);
@@ -295,23 +296,28 @@ void lf_rfid_workaround(void* p) {
                         furiac_exit(NULL);
                     }
 
-                    if(event.value.input.state && event.value.input.input == InputUp) {
+                    if(event.value.input.type == InputTypePress &&
+                       event.value.input.key == InputKeyUp) {
                         state->dirty = true;
                         state->freq_khz += 10;
                     }
 
-                    if(event.value.input.state && event.value.input.input == InputDown) {
+                    if(event.value.input.type == InputTypePress &&
+                       event.value.input.key == InputKeyDown) {
                         state->dirty = true;
                         state->freq_khz -= 10;
                     }
 
-                    if(event.value.input.state && event.value.input.input == InputLeft) {
+                    if(event.value.input.type == InputTypePress &&
+                       event.value.input.key == InputKeyLeft) {
                     }
 
-                    if(event.value.input.state && event.value.input.input == InputRight) {
+                    if(event.value.input.type == InputTypePress &&
+                       event.value.input.key == InputKeyRight) {
                     }
 
-                    if(event.value.input.state && event.value.input.input == InputOk) {
+                    if(event.value.input.type == InputTypePress &&
+                       event.value.input.key == InputKeyOk) {
                         state->dirty = true;
                         state->on = !state->on;
                     }
diff --git a/applications/menu/menu_event.c b/applications/menu/menu_event.c
index eea02905..80512798 100644
--- a/applications/menu/menu_event.c
+++ b/applications/menu/menu_event.c
@@ -55,19 +55,19 @@ void menu_event_input_callback(InputEvent* input_event, void* context) {
     MenuEvent* menu_event = context;
     MenuMessage message;
 
-    if(!input_event->state) return;
+    if(input_event->type != InputTypeShort) return;
 
-    if(input_event->input == InputUp) {
+    if(input_event->key == InputKeyUp) {
         message.type = MenuMessageTypeUp;
-    } else if(input_event->input == InputDown) {
+    } else if(input_event->key == InputKeyDown) {
         message.type = MenuMessageTypeDown;
-    } else if(input_event->input == InputRight) {
+    } else if(input_event->key == InputKeyRight) {
         message.type = MenuMessageTypeRight;
-    } else if(input_event->input == InputLeft) {
+    } else if(input_event->key == InputKeyLeft) {
         message.type = MenuMessageTypeLeft;
-    } else if(input_event->input == InputOk) {
+    } else if(input_event->key == InputKeyOk) {
         message.type = MenuMessageTypeOk;
-    } else if(input_event->input == InputBack) {
+    } else if(input_event->key == InputKeyBack) {
         message.type = MenuMessageTypeBack;
     } else {
         message.type = MenuMessageTypeUnknown;
diff --git a/applications/music-player/music-player.c b/applications/music-player/music-player.c
index c12408f0..453724b3 100644
--- a/applications/music-player/music-player.c
+++ b/applications/music-player/music-player.c
@@ -357,7 +357,7 @@ void music_player_thread(void* p) {
 }
 
 void music_player(void* p) {
-    osMessageQueueId_t event_queue = osMessageQueueNew(1, sizeof(MusicDemoEvent), NULL);
+    osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(MusicDemoEvent), NULL);
 
     State _state;
     _state.note_record = NULL;
@@ -384,7 +384,7 @@ void music_player(void* p) {
     // open input record
     PubSub* input_events_record = furi_record_open("input_events");
     // prepare "do nothing" event
-    InputEvent input_event = {InputRight, true};
+    InputEvent input_event = {InputKeyRight, true};
 
     // start player thread
     // TODO change to fuirac_start
@@ -406,24 +406,29 @@ void music_player(void* p) {
         if(event_status == osOK) {
             if(event.type == EventTypeKey) {
                 // press events
-                if(event.value.input.state && event.value.input.input == InputBack) {
+                if(event.value.input.type == InputTypePress &&
+                   event.value.input.key == InputKeyBack) {
                 }
 
-                if(event.value.input.state && event.value.input.input == InputUp) {
+                if(event.value.input.type == InputTypePress &&
+                   event.value.input.key == InputKeyUp) {
                     if(state->volume_id < state->volume_id_max - 1) state->volume_id++;
                 }
 
-                if(event.value.input.state && event.value.input.input == InputDown) {
+                if(event.value.input.type == InputTypePress &&
+                   event.value.input.key == InputKeyDown) {
                     if(state->volume_id > 0) state->volume_id--;
                 }
 
-                if(event.value.input.state && event.value.input.input == InputLeft) {
+                if(event.value.input.type == InputTypePress &&
+                   event.value.input.key == InputKeyLeft) {
                 }
 
-                if(event.value.input.state && event.value.input.input == InputRight) {
+                if(event.value.input.type == InputTypePress &&
+                   event.value.input.key == InputKeyRight) {
                 }
 
-                if(event.value.input.input == InputOk) {
+                if(event.value.input.key == InputKeyOk) {
                 }
 
             } else if(event.type == EventTypeNote) {
diff --git a/applications/sd-card-test/sd-card-test.cpp b/applications/sd-card-test/sd-card-test.cpp
index e2e73668..495cc28e 100644
--- a/applications/sd-card-test/sd-card-test.cpp
+++ b/applications/sd-card-test/sd-card-test.cpp
@@ -57,8 +57,8 @@ public:
     void render(Canvas* canvas);
     template  void set_text(std::initializer_list list);
     template  void set_error(std::initializer_list list);
-    void wait_for_button(Input input_button);
-    bool ask(Input input_button_cancel, Input input_button_ok);
+    void wait_for_button(InputKey input_button);
+    bool ask(InputKey input_button_cancel, InputKey input_button_ok);
     void blink_red();
     void set_red();
     void blink_green();
@@ -145,7 +145,7 @@ void SdTest::run() {
         "",
         "press BACK to exit",
     });
-    wait_for_button(InputBack);
+    wait_for_button(InputKeyBack);
     exit();
 }
 
@@ -184,9 +184,9 @@ void SdTest::show_warning() {
          "",
          "press UP DOWN OK to continue"});
 
-    wait_for_button(InputUp);
-    wait_for_button(InputDown);
-    wait_for_button(InputOk);
+    wait_for_button(InputKeyUp);
+    wait_for_button(InputKeyDown);
+    wait_for_button(InputKeyOk);
 }
 
 // get info about sd card, label, sn
@@ -216,7 +216,7 @@ void SdTest::get_sd_card_info() {
 
     blink_green();
 
-    wait_for_button(InputOk);
+    wait_for_button(InputKeyOk);
 }
 
 // prepare benchmark data (allocate data in ram)
@@ -296,7 +296,7 @@ void SdTest::write_benchmark() {
 
     blink_green();
 
-    wait_for_button(InputOk);
+    wait_for_button(InputKeyOk);
 }
 
 uint32_t SdTest::write_benchmark_internal(const uint32_t size, const uint32_t count, bool silent) {
@@ -436,7 +436,7 @@ void SdTest::read_benchmark() {
 
     blink_green();
 
-    wait_for_button(InputOk);
+    wait_for_button(InputKeyOk);
 }
 
 uint32_t SdTest::read_benchmark_internal(
@@ -590,7 +590,7 @@ void SdTest::hash_benchmark() {
 
     blink_green();
 
-    wait_for_button(InputOk);
+    wait_for_button(InputKeyOk);
 }
 
 void SdTest::cli_read_benchmark(string_t args, void* _ctx) {
@@ -786,18 +786,18 @@ void SdTest::cli_write_benchmark(string_t args, void* _ctx) {
 }
 
 // wait for button press
-void SdTest::wait_for_button(Input input_button) {
+void SdTest::wait_for_button(InputKey input_button) {
     SdTestEvent event;
     osMessageQueueReset(event_queue);
     while(1) {
         osStatus_t result = osMessageQueueGet(event_queue, &event, NULL, osWaitForever);
 
         if(result == osOK && event.type == SdTestEvent::EventTypeKey) {
-            if(event.value.input.state == true) {
-                if(event.value.input.input == InputBack) {
+            if(event.value.input.type == InputTypeShort) {
+                if(event.value.input.key == InputKeyBack) {
                     exit();
                 } else {
-                    if(event.value.input.input == input_button) {
+                    if(event.value.input.key == input_button) {
                         blink_green();
                         break;
                     } else {
@@ -811,7 +811,7 @@ void SdTest::wait_for_button(Input input_button) {
 }
 
 // ask user to proceed or cancel
-bool SdTest::ask(Input input_button_cancel, Input input_button_ok) {
+bool SdTest::ask(InputKey input_button_cancel, InputKey input_button_ok) {
     bool return_result;
     SdTestEvent event;
     osMessageQueueReset(event_queue);
@@ -819,15 +819,15 @@ bool SdTest::ask(Input input_button_cancel, Input input_button_ok) {
         osStatus_t result = osMessageQueueGet(event_queue, &event, NULL, osWaitForever);
 
         if(result == osOK && event.type == SdTestEvent::EventTypeKey) {
-            if(event.value.input.state == true) {
-                if(event.value.input.input == InputBack) {
+            if(event.value.input.type == InputTypeShort) {
+                if(event.value.input.key == InputKeyBack) {
                     exit();
                 } else {
-                    if(event.value.input.input == input_button_ok) {
+                    if(event.value.input.key == input_button_ok) {
                         blink_green();
                         return_result = true;
                         break;
-                    } else if(event.value.input.input == input_button_cancel) {
+                    } else if(event.value.input.key == input_button_cancel) {
                         blink_green();
                         return_result = false;
                         break;
@@ -865,7 +865,7 @@ void SdTest::blink_green() {
 template  void SdTest::set_error(std::initializer_list list) {
     set_text(list);
     set_red();
-    wait_for_button(InputBack);
+    wait_for_button(InputKeyBack);
     exit();
 }
 
diff --git a/applications/sd-filesystem/sd-filesystem.c b/applications/sd-filesystem/sd-filesystem.c
index 4afa1165..3f4b1818 100644
--- a/applications/sd-filesystem/sd-filesystem.c
+++ b/applications/sd-filesystem/sd-filesystem.c
@@ -105,7 +105,7 @@ SdApp* sd_app_alloc() {
         furiac_exit(NULL);
     }
 
-    sd_app->event_queue = osMessageQueueNew(1, sizeof(InputEvent), NULL);
+    sd_app->event_queue = osMessageQueueNew(8, sizeof(InputEvent), NULL);
 
     // init view_port
     sd_app->view_port = view_port_alloc();
@@ -127,7 +127,7 @@ SdApp* sd_app_alloc() {
     return sd_app;
 }
 
-bool app_sd_ask(SdApp* sd_app, Input input_true, Input input_false) {
+bool app_sd_ask(SdApp* sd_app, InputKey input_true, InputKey input_false) {
     bool result;
 
     InputEvent event;
@@ -136,11 +136,11 @@ bool app_sd_ask(SdApp* sd_app, Input input_true, Input input_false) {
             osMessageQueueGet(sd_app->event_queue, &event, NULL, osWaitForever);
 
         if(event_status == osOK) {
-            if(event.state && event.input == input_true) {
+            if(event.type == InputTypeShort && event.key == input_true) {
                 result = true;
                 break;
             }
-            if(event.state && event.input == InputBack) {
+            if(event.type == InputTypeShort && event.key == InputKeyBack) {
                 result = false;
                 break;
             }
@@ -254,7 +254,7 @@ void app_sd_info_callback(void* context) {
             str_buffer[5]);
     }
 
-    app_sd_ask(sd_app, InputBack, InputBack);
+    app_sd_ask(sd_app, InputKeyBack, InputKeyBack);
 
     sd_set_lines(sd_app, 0);
     view_port_enabled_set(sd_app->view_port, false);
@@ -294,7 +294,7 @@ void app_sd_format_callback(void* context) {
     view_port_enabled_set(sd_app->view_port, true);
 
     // wait for input
-    if(!app_sd_ask(sd_app, InputUp, InputBack)) {
+    if(!app_sd_ask(sd_app, InputKeyUp, InputKeyBack)) {
         view_port_enabled_set(sd_app->view_port, false);
         return;
     }
@@ -313,7 +313,7 @@ void app_sd_format_callback(void* context) {
     }
 
     // wait for BACK
-    app_sd_ask(sd_app, InputBack, InputBack);
+    app_sd_ask(sd_app, InputKeyBack, InputKeyBack);
 
     view_port_enabled_set(sd_app->view_port, false);
 }
@@ -357,7 +357,7 @@ void app_sd_eject_callback(void* context) {
     sd_set_lines(sd_app, 1, "SD card can be pulled out");
 
     // wait for BACK
-    app_sd_ask(sd_app, InputBack, InputBack);
+    app_sd_ask(sd_app, InputKeyBack, InputKeyBack);
 
     view_port_enabled_set(sd_app->view_port, false);
 }
diff --git a/applications/sd-nfc/sdnfc.cpp b/applications/sd-nfc/sdnfc.cpp
index 1a63715a..032afa1b 100644
--- a/applications/sd-nfc/sdnfc.cpp
+++ b/applications/sd-nfc/sdnfc.cpp
@@ -105,7 +105,8 @@ void AppSdNFC::run() {
         if(get_event(&event, 1000)) {
             if(event.type == AppSdNFCEvent::EventTypeKey) {
                 // press events
-                if(event.value.input.state && event.value.input.input == InputBack) {
+                if(event.value.input.type == InputTypeShort &&
+                   event.value.input.key == InputKeyBack) {
                     exit();
                 }
             }
diff --git a/applications/template/template.c.example b/applications/template/template.c.example
index d86805c9..7cd7975b 100644
--- a/applications/template/template.c.example
+++ b/applications/template/template.c.example
@@ -38,7 +38,7 @@ static void input_callback(InputEvent* input_event, void* ctx) {
 }
 
 void template_app(void* p) {
-    osMessageQueueId_t event_queue = osMessageQueueNew(1, sizeof(Event), NULL);
+    osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(Event), NULL);
 
     State _state;
     /* init state here */
@@ -72,22 +72,22 @@ void template_app(void* p) {
         if(event_status == osOK) {
             if(event.type == EventTypeKey) {
                 // press events
-                if(event.value.input.state && event.value.input.input == InputBack) {
+                if(event.value.input.type == InputTypeShort && event.value.input.key == InputKeyBack) {
                 }
 
-                if(event.value.input.state && event.value.input.input == InputUp) {
+                if(event.value.input.type == InputTypeShort && event.value.input.key == InputKeyUp) {
                 }
 
-                if(event.value.input.state && event.value.input.input == InputDown) {
+                if(event.value.input.type == InputTypeShort && event.value.input.key == InputKeyDown) {
                 }
 
-                if(event.value.input.state && event.value.input.input == InputLeft) {
+                if(event.value.input.type == InputTypeShort && event.value.input.key == InputKeyLeft) {
                 }
 
-                if(event.value.input.state && event.value.input.input == InputRight) {
+                if(event.value.input.type == InputTypeShort && event.value.input.key == InputKeyRight) {
                 }
 
-                if(event.value.input.input == InputOk) {
+                if(event.value.input.key == InputKeyOk) {
                 }
             }
         } else {
diff --git a/firmware/targets/f4/Src/target-resources.c b/firmware/targets/f4/Src/target-resources.c
deleted file mode 100644
index e14619e6..00000000
--- a/firmware/targets/f4/Src/target-resources.c
+++ /dev/null
@@ -1,11 +0,0 @@
-#include "main.h"
-#include 
-
-const bool input_invert[GPIO_INPUT_PINS_COUNT] = {
-    true, // {BUTTON_UP_GPIO_Port, BUTTON_UP_Pin},
-    true, // {BUTTON_DOWN_GPIO_Port, BUTTON_DOWN_Pin},
-    true, // {BUTTON_RIGHT_GPIO_Port, BUTTON_RIGHT_Pin},
-    true, // {BUTTON_LEFT_GPIO_Port, BUTTON_LEFT_Pin},
-    false, // {BUTTON_OK_GPIO_Port, BUTTON_OK_Pin},
-    true, // {BUTTON_BACK_GPIO_Port, BUTTON_BACK_Pin},
-};
diff --git a/firmware/targets/f4/api-hal/api-hal-resources.c b/firmware/targets/f4/api-hal/api-hal-resources.c
index d65d0fb6..369e9f4b 100644
--- a/firmware/targets/f4/api-hal/api-hal-resources.c
+++ b/firmware/targets/f4/api-hal/api-hal-resources.c
@@ -1,15 +1,30 @@
+#include 
 #include "main.h"
 #include 
 
-const GpioPin input_gpio[GPIO_INPUT_PINS_COUNT] = {
-    {BUTTON_UP_GPIO_Port, BUTTON_UP_Pin},
-    {BUTTON_DOWN_GPIO_Port, BUTTON_DOWN_Pin},
-    {BUTTON_RIGHT_GPIO_Port, BUTTON_RIGHT_Pin},
-    {BUTTON_LEFT_GPIO_Port, BUTTON_LEFT_Pin},
-    {BUTTON_OK_GPIO_Port, BUTTON_OK_Pin},
-    {BUTTON_BACK_GPIO_Port, BUTTON_BACK_Pin},
+const InputPin input_pins[] = {
+    {.port = BUTTON_UP_GPIO_Port, .pin = BUTTON_UP_Pin, .key = InputKeyUp, .inverted = true},
+    {.port = BUTTON_DOWN_GPIO_Port,
+     .pin = BUTTON_DOWN_Pin,
+     .key = InputKeyDown,
+     .inverted = true},
+    {.port = BUTTON_RIGHT_GPIO_Port,
+     .pin = BUTTON_RIGHT_Pin,
+     .key = InputKeyRight,
+     .inverted = true},
+    {.port = BUTTON_LEFT_GPIO_Port,
+     .pin = BUTTON_LEFT_Pin,
+     .key = InputKeyLeft,
+     .inverted = true},
+    {.port = BUTTON_OK_GPIO_Port, .pin = BUTTON_OK_Pin, .key = InputKeyOk, .inverted = false},
+    {.port = BUTTON_BACK_GPIO_Port,
+     .pin = BUTTON_BACK_Pin,
+     .key = InputKeyBack,
+     .inverted = true},
 };
 
+const size_t input_pins_count = sizeof(input_pins) / sizeof(InputPin);
+
 const GpioPin led_gpio[3] = {
     {LED_RED_GPIO_Port, LED_RED_Pin},
     {LED_GREEN_GPIO_Port, LED_GREEN_Pin},
diff --git a/firmware/targets/f4/api-hal/api-hal-resources.h b/firmware/targets/f4/api-hal/api-hal-resources.h
index eaedc85b..53b3f7e5 100644
--- a/firmware/targets/f4/api-hal/api-hal-resources.h
+++ b/firmware/targets/f4/api-hal/api-hal-resources.h
@@ -2,11 +2,28 @@
 #include "main.h"
 #include 
 
-#define DEBOUNCE_TICKS 10
-#define GPIO_INPUT_PINS_COUNT 6
+/* Input Related Constants */
+#define INPUT_DEBOUNCE_TICKS 20
 
-extern const GpioPin input_gpio[GPIO_INPUT_PINS_COUNT];
-extern const bool input_invert[GPIO_INPUT_PINS_COUNT];
+/* Input Keys */
+typedef enum {
+    InputKeyUp,
+    InputKeyDown,
+    InputKeyRight,
+    InputKeyLeft,
+    InputKeyOk,
+    InputKeyBack,
+} InputKey;
+
+typedef struct {
+    const GPIO_TypeDef* port;
+    const uint16_t pin;
+    const InputKey key;
+    const bool inverted;
+} InputPin;
+
+extern const InputPin input_pins[];
+extern const size_t input_pins_count;
 
 extern const GpioPin led_gpio[3];
 extern const GpioPin backlight_gpio;
diff --git a/lib/ST25RFAL002/platform.c b/lib/ST25RFAL002/platform.c
index 00ffb0b6..8c51e7dc 100644
--- a/lib/ST25RFAL002/platform.c
+++ b/lib/ST25RFAL002/platform.c
@@ -1,14 +1,18 @@
 #include "platform.h"
 #include 
 #include 
+#include 
 #include 
 
 static osThreadAttr_t platform_irq_thread_attr;
 static volatile osThreadId_t platform_irq_thread_id = NULL;
 static volatile PlatformIrqCallback platform_irq_callback = NULL;
 
-void nfc_isr() {
-    if(platform_irq_callback && platformGpioIsHigh( ST25R_INT_PORT, ST25R_INT_PIN )) {
+void nfc_isr(void* _pin, void* _ctx) {
+    uint32_t pin = (uint32_t)_pin;
+    if(pin == NFC_IRQ_Pin
+        && platform_irq_callback
+        && platformGpioIsHigh(ST25R_INT_PORT, ST25R_INT_PIN)) {
         osThreadFlagsSet(platform_irq_thread_id, 0x1);
     }
 }
@@ -28,6 +32,7 @@ void platformSetIrqCallback(PlatformIrqCallback callback) {
     platform_irq_thread_attr.stack_size = 512;
     platform_irq_thread_attr.priority = osPriorityISR;
     platform_irq_thread_id = osThreadNew(platformIrqWorker, NULL, &platform_irq_thread_attr);
+    api_interrupt_add(nfc_isr, InterruptTypeExternalInterrupt, NULL);
 }
 
 HAL_StatusTypeDef platformSpiTxRx(const uint8_t *txBuf, uint8_t *rxBuf, uint16_t len) {
diff --git a/lib/app-template/app-template.cpp b/lib/app-template/app-template.cpp
index b04210f1..d085f0c5 100644
--- a/lib/app-template/app-template.cpp
+++ b/lib/app-template/app-template.cpp
@@ -65,12 +65,12 @@ void AppExample::run() {
         if(get_event(&event, 1000)) {
             if(event.type == AppExampleEvent::EventTypeKey) {
                 // press events
-                if(event.value.input.state && event.value.input.input == InputBack) {
+                if(event.value.input.type == InputTypeShort && event.value.input.key == InputKeyBack) {
                     printf("bye!\n");
                     exit();
                 }
 
-                if(event.value.input.state && event.value.input.input == InputUp) {
+                if(event.value.input.type == InputTypeShort && event.value.input.key == InputKeyUp) {
                     // to read or write state you need to execute
                     // acquire modify release state
                     acquire_state();