diff --git a/src/config.rs b/src/config.rs
index 343e429..c290797 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -19,7 +19,8 @@ use crate::{
     helpers::{PackedFloat, ToPackedFloatArray, ToRegularArray, XyValuePair},
     hid::gcc::{GcButtons1, GcButtons2, GcState},
     input::{
-        read_ext_adc, Stick, StickAxis, FLOAT_ORIGIN, SPI_ACS_SHARED, SPI_CCS_SHARED, SPI_SHARED,
+        read_ext_adc, ControllerState, Stick, StickAxis, FLOAT_ORIGIN, SPI_ACS_SHARED,
+        SPI_CCS_SHARED, SPI_SHARED,
     },
     stick::{
         calc_stick_values, legalize_notches, AppliedCalibration, CleanedCalibrationPoints,
@@ -37,7 +38,7 @@ use embassy_sync::{
 };
 use embassy_time::Timer;
 
-use crate::input::CHANNEL_GCC_STATE;
+use crate::input::CHANNEL_CONTROLLER_STATE;
 
 /// Whether we are currently calibrating the sticks. Updates are dispatched when the status changes.
 /// Initial status is assumed to be false.
@@ -53,8 +54,10 @@ pub static SIGNAL_OVERRIDE_STICK_STATE: Signal<
 > = Signal::new();
 
 /// Dispatched when we want to override the GCC state for a short amount of time.
-pub static SIGNAL_OVERRIDE_GCC_STATE: Signal<CriticalSectionRawMutex, OverrideGcReportInstruction> =
-    Signal::new();
+pub static SIGNAL_OVERRIDE_CONTROLLER_STATE: Signal<
+    CriticalSectionRawMutex,
+    OverrideGcReportInstruction,
+> = Signal::new();
 
 /// Dispatched when we want to enter config mode, sent from core1 so config mode
 /// doesn't need to watch for the entire button combo every time.
@@ -79,7 +82,7 @@ const MAX_ANALOG_SCALER: u8 = 125;
 /// a certain mode.
 #[derive(Default, Debug, Clone, Format)]
 pub struct OverrideGcReportInstruction {
-    pub report: GcState,
+    pub report: ControllerState,
     pub duration_ms: u64,
 }
 
@@ -684,7 +687,7 @@ trait ButtonPressProvider {
 }
 
 impl<'a, T: RawMutex, const I: usize, const J: usize, const K: usize> ButtonPressProvider
-    for Subscriber<'a, T, GcState, I, J, K>
+    for Subscriber<'a, T, ControllerState, I, J, K>
 {
     async fn wait_for_button_press(&mut self, button_to_wait_for: &AwaitableButtons) {
         loop {
@@ -784,25 +787,25 @@ impl<'a, T: RawMutex, const I: usize, const J: usize, const K: usize> ButtonPres
 }
 
 pub fn is_awaitable_button_pressed(
-    report: &GcState,
+    report: &ControllerState,
     button_to_wait_for: &AwaitableButtons,
 ) -> bool {
     match button_to_wait_for {
-        AwaitableButtons::A => report.buttons_1.button_a,
-        AwaitableButtons::B => report.buttons_1.button_b,
-        AwaitableButtons::X => report.buttons_1.button_x,
-        AwaitableButtons::Y => report.buttons_1.button_y,
-        AwaitableButtons::Up => report.buttons_1.dpad_up,
-        AwaitableButtons::Down => report.buttons_1.dpad_down,
-        AwaitableButtons::Left => report.buttons_1.dpad_left,
-        AwaitableButtons::Right => report.buttons_1.dpad_right,
-        AwaitableButtons::Start => report.buttons_2.button_start,
-        AwaitableButtons::L => report.buttons_2.button_l || report.trigger_l > 10,
-        AwaitableButtons::R => report.buttons_2.button_r || report.trigger_r > 10,
-        AwaitableButtons::Z => report.buttons_2.button_z,
+        AwaitableButtons::A => report.button_a,
+        AwaitableButtons::B => report.button_b,
+        AwaitableButtons::X => report.button_x,
+        AwaitableButtons::Y => report.button_y,
+        AwaitableButtons::Up => report.dpad_up,
+        AwaitableButtons::Down => report.dpad_down,
+        AwaitableButtons::Left => report.dpad_left,
+        AwaitableButtons::Right => report.dpad_right,
+        AwaitableButtons::Start => report.button_start,
+        AwaitableButtons::L => report.trigger_l,
+        AwaitableButtons::R => report.trigger_r,
+        AwaitableButtons::Z => report.trigger_zr,
         AwaitableButtons::Wildcard => true,
         AwaitableButtons::Impossible => false,
-        AwaitableButtons::NotZ => !report.buttons_2.button_z,
+        AwaitableButtons::NotZ => !report.trigger_zr,
     }
 }
 
@@ -1023,7 +1026,7 @@ impl<'a> StickCalibrationProcess<'a> {
     pub async fn calibrate_stick(&mut self) {
         info!("Beginning stick calibration for {}", self.which_stick);
 
-        let mut gcc_subscriber = CHANNEL_GCC_STATE.subscriber().unwrap();
+        let mut gcc_subscriber = CHANNEL_CONTROLLER_STATE.subscriber().unwrap();
         SIGNAL_IS_CALIBRATING.signal(true);
 
         let mut done = false;
@@ -1137,7 +1140,7 @@ fn get_stick_display_coords(current_step: usize) -> (f32, f32) {
 }
 
 pub async fn override_gcc_state_and_wait(state: &OverrideGcReportInstruction) {
-    SIGNAL_OVERRIDE_GCC_STATE.signal(state.clone());
+    SIGNAL_OVERRIDE_CONTROLLER_STATE.signal(state.clone());
     Timer::after_millis(state.duration_ms).await;
 }
 
@@ -1150,7 +1153,7 @@ async fn configuration_main_loop<
 >(
     current_config: &ControllerConfig,
     flash: &mut Flash<'static, FLASH, Async, FLASH_SIZE>,
-    gcc_subscriber: &mut Subscriber<'a, M, GcState, C, S, P>,
+    gcc_subscriber: &mut Subscriber<'a, M, ControllerState, C, S, P>,
 ) -> ControllerConfig {
     let mut final_config = current_config.clone();
     let config_options = [
@@ -1226,7 +1229,8 @@ async fn configuration_main_loop<
                         stick_y: 127,
                         cstick_x: 127,
                         cstick_y: 127,
-                    },
+                    }
+                    .into(),
                     duration_ms: 1000,
                 })
                 .await;
@@ -1254,7 +1258,8 @@ async fn configuration_main_loop<
                         stick_y: 255,
                         cstick_x: 127,
                         cstick_y: 127,
-                    },
+                    }
+                    .into(),
                     duration_ms: 1000,
                 })
                 .await;
@@ -1283,7 +1288,8 @@ async fn configuration_main_loop<
                         stick_y: 127,
                         cstick_x: 255,
                         cstick_y: 255,
-                    },
+                    }
+                    .into(),
                     duration_ms: 1000,
                 })
                 .await;
@@ -1370,7 +1376,8 @@ async fn configuration_main_loop<
                                     StickAxis::YAxis => *to_adjust,
                                 },
                             }) as u8,
-                    },
+                    }
+                    .into(),
                     duration_ms: 750,
                 })
                 .await;
@@ -1456,7 +1463,8 @@ async fn configuration_main_loop<
                                     StickAxis::YAxis => *to_adjust,
                                 },
                             }) as u8,
-                    },
+                    }
+                    .into(),
                     duration_ms: 750,
                 })
                 .await;
@@ -1542,7 +1550,8 @@ async fn configuration_main_loop<
                                     StickAxis::YAxis => *to_adjust,
                                 },
                             }) as u8,
-                    },
+                    }
+                    .into(),
                     duration_ms: 750,
                 })
                 .await;
@@ -1597,7 +1606,8 @@ async fn configuration_main_loop<
                                 Stick::ControlStick => 0,
                                 Stick::CStick => *to_adjust,
                             }) as u8,
-                    },
+                    }
+                    .into(),
                     duration_ms: 750,
                 })
                 .await;
@@ -1653,7 +1663,8 @@ async fn configuration_main_loop<
                                 Stick::ControlStick => 0,
                                 Stick::CStick => *to_adjust,
                             }) as u8,
-                    },
+                    }
+                    .into(),
                     duration_ms: 750,
                 })
                 .await;
@@ -1694,7 +1705,8 @@ async fn configuration_main_loop<
                         stick_y: 127 + *to_adjust,
                         cstick_x: 127,
                         cstick_y: 127,
-                    },
+                    }
+                    .into(),
                     duration_ms: 750,
                 })
                 .await;
@@ -1735,7 +1747,8 @@ async fn configuration_main_loop<
                             }) as u8,
                         cstick_x: 127,
                         cstick_y: 127,
-                    },
+                    }
+                    .into(),
                     duration_ms: 750,
                 })
                 .await;
@@ -1763,7 +1776,8 @@ async fn configuration_main_loop<
                         stick_y: 127 + final_config.astick_config.y_waveshaping,
                         cstick_x: 127 + final_config.cstick_config.x_waveshaping,
                         cstick_y: 127 + final_config.cstick_config.y_waveshaping,
-                    },
+                    }
+                    .into(),
                     duration_ms: 1000,
                 })
                 .await;
@@ -1789,7 +1803,8 @@ async fn configuration_main_loop<
                         stick_y: 127 + final_config.astick_config.y_smoothing,
                         cstick_x: 127 + final_config.cstick_config.x_smoothing,
                         cstick_y: 127 + final_config.cstick_config.y_smoothing,
-                    },
+                    }
+                    .into(),
                     duration_ms: 1000,
                 })
                 .await;
@@ -1815,7 +1830,8 @@ async fn configuration_main_loop<
                         stick_y: (127 + final_config.astick_config.y_snapback) as u8,
                         cstick_x: (127 + final_config.cstick_config.x_snapback) as u8,
                         cstick_y: (127 + final_config.cstick_config.y_snapback) as u8,
-                    },
+                    }
+                    .into(),
                     duration_ms: 1000,
                 })
                 .await;
@@ -1836,7 +1852,7 @@ async fn configuration_main_loop<
 
 #[embassy_executor::task]
 pub async fn config_task(mut flash: Flash<'static, FLASH, Async, FLASH_SIZE>) {
-    let mut gcc_subscriber = CHANNEL_GCC_STATE.subscriber().unwrap();
+    let mut gcc_subscriber = CHANNEL_CONTROLLER_STATE.subscriber().unwrap();
 
     info!("Config task is running.");
 
@@ -1886,7 +1902,8 @@ pub async fn config_task(mut flash: Flash<'static, FLASH, Async, FLASH_SIZE>) {
                 stick_y: 127,
                 cstick_x: 127,
                 cstick_y: 127,
-            },
+            }
+            .into(),
             duration_ms: 1000,
         })
         .await;
@@ -1902,7 +1919,7 @@ pub async fn config_task(mut flash: Flash<'static, FLASH, Async, FLASH_SIZE>) {
 
 #[embassy_executor::task]
 pub async fn enter_config_mode_task() {
-    let mut gcc_subscriber = CHANNEL_GCC_STATE.subscriber().unwrap();
+    let mut gcc_subscriber = CHANNEL_CONTROLLER_STATE.subscriber().unwrap();
 
     info!("Enter config mode task is running.");
 
diff --git a/src/hid/gcc.rs b/src/hid/gcc.rs
index dddc8ef..c8cd030 100644
--- a/src/hid/gcc.rs
+++ b/src/hid/gcc.rs
@@ -4,7 +4,10 @@ use embassy_usb::{
     control::OutResponse,
 };
 
-use crate::usb_comms::{HidReportBuilder, SIGNAL_RUMBLE};
+use crate::{
+    input::ControllerState,
+    usb_comms::{HidReportBuilder, SIGNAL_RUMBLE},
+};
 use packed_struct::{derive::PackedStruct, PackedStruct};
 
 #[rustfmt::skip]
@@ -125,6 +128,36 @@ pub struct GcState {
     pub trigger_r: u8,
 }
 
+impl From<ControllerState> for GcState {
+    fn from(value: ControllerState) -> Self {
+        Self {
+            buttons_1: GcButtons1 {
+                button_a: value.button_a,
+                button_b: value.button_b,
+                button_x: value.button_x,
+                button_y: value.button_y,
+                dpad_left: value.dpad_left,
+                dpad_right: value.dpad_right,
+                dpad_down: value.dpad_down,
+                dpad_up: value.dpad_up,
+            },
+            buttons_2: GcButtons2 {
+                button_start: value.button_start,
+                button_z: value.trigger_zr,
+                button_r: value.trigger_r,
+                button_l: value.trigger_l,
+                blank1: 0,
+            },
+            stick_x: value.stick_state.ax,
+            stick_y: value.stick_state.ay,
+            cstick_x: value.stick_state.cx,
+            cstick_y: value.stick_state.cy,
+            trigger_l: if value.trigger_l { 255 } else { 0 },
+            trigger_r: if value.trigger_r { 255 } else { 0 },
+        }
+    }
+}
+
 impl Default for GcState {
     fn default() -> Self {
         Self {
@@ -146,13 +179,15 @@ pub struct GcReportBuilder {
 }
 
 impl HidReportBuilder<37> for GcReportBuilder {
-    async fn get_hid_report(&mut self, state: &GcState) -> [u8; 37] {
+    async fn get_hid_report(&mut self, state: &ControllerState) -> [u8; 37] {
         let mut buffer = [0u8; 37];
 
         buffer[0] = 0x21;
         buffer[1] |= 0x14;
 
-        let data = state.pack().expect("Failed to pack GC input data");
+        let gcc_state: GcState = (*state).into();
+
+        let data = gcc_state.pack().expect("Failed to pack GC input data");
 
         if !self.gc_first {
             buffer[1] |= 0x04;
diff --git a/src/hid/procon.rs b/src/hid/procon.rs
index 474cfdf..1ed2b5a 100644
--- a/src/hid/procon.rs
+++ b/src/hid/procon.rs
@@ -18,9 +18,7 @@ use embassy_usb::{
 use packed_struct::{derive::PackedStruct, PackedStruct};
 use rand::RngCore;
 
-use crate::usb_comms::HidReportBuilder;
-
-use super::gcc::GcState;
+use crate::{input::ControllerState, usb_comms::HidReportBuilder};
 
 const SW_INFO_SET_MAC: u8 = 0x01;
 
@@ -486,36 +484,36 @@ impl Default for BatteryStatus {
     }
 }
 
-impl From<&GcState> for ProconState {
-    fn from(value: &GcState) -> Self {
+impl From<&ControllerState> for ProconState {
+    fn from(value: &ControllerState) -> Self {
         Self {
             buttons_left: ProconButtonsLeft {
-                dpad_down: value.buttons_1.dpad_down,
-                dpad_right: value.buttons_1.dpad_right,
-                dpad_up: value.buttons_1.dpad_up,
-                dped_left: value.buttons_1.dpad_left,
-                trigger_l: value.buttons_2.button_l,
-                trigger_zl: value.buttons_2.button_l,
+                dpad_down: value.dpad_down,
+                dpad_right: value.dpad_right,
+                dpad_up: value.dpad_up,
+                dped_left: value.dpad_left,
+                trigger_l: value.trigger_zl,
+                trigger_zl: value.trigger_l,
                 ..Default::default()
             },
             buttons_right: ProconButtonsRight {
-                button_a: value.buttons_1.button_a,
-                button_b: value.buttons_1.button_b,
-                button_x: value.buttons_1.button_x,
-                button_y: value.buttons_1.button_y,
-                trigger_r: value.buttons_2.button_z,
-                trigger_zr: value.buttons_2.button_r,
+                button_a: value.button_a,
+                button_b: value.button_b,
+                button_x: value.button_x,
+                button_y: value.button_y,
+                trigger_r: value.trigger_zr,
+                trigger_zr: value.trigger_r,
                 ..Default::default()
             },
             buttons_shared: ProconButtonsShared {
-                button_plus: value.buttons_2.button_start && !value.buttons_2.button_z,
-                button_home: value.buttons_2.button_start && value.buttons_2.button_z,
+                button_plus: value.button_start && !value.trigger_zr,
+                button_home: value.button_start && value.trigger_zr,
                 ..Default::default()
             },
-            lstick_x: value.stick_x as u16 * 16,
-            lstick_y: value.stick_y,
-            rstick_x: value.cstick_x as u16 * 16,
-            rstick_y: value.cstick_y,
+            lstick_x: value.stick_state.ax as u16 * 16,
+            lstick_y: value.stick_state.ay,
+            rstick_x: value.stick_state.cx as u16 * 16,
+            rstick_y: value.stick_state.cy,
         }
     }
 }
@@ -694,7 +692,7 @@ impl ProconReportBuilder {
 }
 
 impl HidReportBuilder<64> for ProconReportBuilder {
-    async fn get_hid_report(&mut self, state: &GcState) -> [u8; 64] {
+    async fn get_hid_report(&mut self, state: &ControllerState) -> [u8; 64] {
         let current_report_info = if self.switch_reporting_mode == RM_SEND_STATE {
             SIGNAL_PROCON_REQUEST.try_take()
         } else {
diff --git a/src/hid/xinput.rs b/src/hid/xinput.rs
index ddd8c20..23138f5 100644
--- a/src/hid/xinput.rs
+++ b/src/hid/xinput.rs
@@ -22,9 +22,9 @@ use embassy_usb::{
 };
 use packed_struct::{derive::PackedStruct, PackedStruct};
 
-use crate::usb_comms::HidReportBuilder;
+use crate::{input::ControllerState, usb_comms::HidReportBuilder};
 
-use super::{gcc::GcState, HidReaderWriterSplit, UsbReader, UsbWriter};
+use super::{HidReaderWriterSplit, UsbReader, UsbWriter};
 
 /// lol
 pub const XINPUT_REPORT_DESCRIPTOR: &[u8] = &[];
@@ -111,37 +111,37 @@ pub struct XInputReport {
     pub reserved: [u8; 18],
 }
 
-impl From<&GcState> for XInputReport {
-    fn from(value: &GcState) -> Self {
+impl From<&ControllerState> for XInputReport {
+    fn from(value: &ControllerState) -> Self {
         Self {
             report_id: 0,
             report_size: 20,
             buttons_1: XInputButtons1 {
-                dpad_up: value.buttons_1.dpad_up,
-                dpad_down: value.buttons_1.dpad_down,
-                dpad_right: value.buttons_1.dpad_right,
-                dpad_left: value.buttons_1.dpad_left,
-                button_menu: value.buttons_2.button_start,
+                dpad_up: value.dpad_up,
+                dpad_down: value.dpad_down,
+                dpad_right: value.dpad_right,
+                dpad_left: value.dpad_left,
+                button_menu: value.button_start,
                 button_back: false,
                 button_stick_l: false,
                 button_stick_r: false,
             },
             buttons_2: XInputButtons2 {
                 blank_1: false,
-                bumper_l: false,
-                bumper_r: value.buttons_2.button_z,
-                button_a: value.buttons_1.button_a,
-                button_b: value.buttons_1.button_b,
-                button_x: value.buttons_1.button_x,
-                button_y: value.buttons_1.button_y,
+                bumper_l: value.trigger_zl,
+                bumper_r: value.trigger_zr,
+                button_a: value.button_a,
+                button_b: value.button_b,
+                button_x: value.button_x,
+                button_y: value.button_y,
                 button_guide: false,
             },
-            analog_trigger_l: value.trigger_l,
-            analog_trigger_r: value.trigger_r,
-            stick_left_x: (value.stick_x as i16 - 127).clamp(-127, 127) * 257,
-            stick_left_y: (value.stick_y as i16 - 127).clamp(-127, 127) * 257,
-            stick_right_x: (value.cstick_x as i16 - 127).clamp(-127, 127) * 257,
-            stick_right_y: (value.cstick_y as i16 - 127).clamp(-127, 127) * 257,
+            analog_trigger_l: if value.trigger_l { 255 } else { 0 },
+            analog_trigger_r: if value.trigger_r { 255 } else { 0 },
+            stick_left_x: (value.stick_state.ax as i16 - 127).clamp(-127, 127) * 257,
+            stick_left_y: (value.stick_state.ay as i16 - 127).clamp(-127, 127) * 257,
+            stick_right_x: (value.stick_state.cx as i16 - 127).clamp(-127, 127) * 257,
+            stick_right_y: (value.stick_state.cy as i16 - 127).clamp(-127, 127) * 257,
             reserved: [0u8; 18],
         }
     }
@@ -153,7 +153,7 @@ impl From<&GcState> for XInputReport {
 pub struct XInputReportBuilder;
 
 impl HidReportBuilder<32> for XInputReportBuilder {
-    async fn get_hid_report(&mut self, state: &super::gcc::GcState) -> [u8; 32] {
+    async fn get_hid_report(&mut self, state: &ControllerState) -> [u8; 32] {
         XInputReport::from(state)
             .pack()
             .expect("Failed to pack XInput State")
diff --git a/src/input.rs b/src/input.rs
index 3738ae0..cd28a00 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -17,8 +17,8 @@ use libm::{fmaxf, fminf};
 use crate::{
     config::{
         ControllerConfig, ControllerMode, InputConsistencyMode, OverrideGcReportInstruction,
-        OverrideStickState, SIGNAL_CONFIG_CHANGE, SIGNAL_IS_CALIBRATING, SIGNAL_OVERRIDE_GCC_STATE,
-        SIGNAL_OVERRIDE_STICK_STATE,
+        OverrideStickState, SIGNAL_CONFIG_CHANGE, SIGNAL_IS_CALIBRATING,
+        SIGNAL_OVERRIDE_CONTROLLER_STATE, SIGNAL_OVERRIDE_STICK_STATE,
     },
     filter::{run_waveshaping, FilterGains, KalmanState, WaveshapingValues, FILTER_GAINS},
     helpers::XyValuePair,
@@ -29,8 +29,13 @@ use crate::{
 };
 
 /// Used to send the button state to the usb task and the calibration task
-pub static CHANNEL_GCC_STATE: PubSubChannel<CriticalSectionRawMutex, GcState, 1, 4, 1> =
-    PubSubChannel::new();
+pub static CHANNEL_CONTROLLER_STATE: PubSubChannel<
+    CriticalSectionRawMutex,
+    ControllerState,
+    1,
+    4,
+    1,
+> = PubSubChannel::new();
 
 /// Used to send the stick state from the stick task to the main input task
 static SIGNAL_STICK_STATE: Signal<CriticalSectionRawMutex, StickState> = Signal::new();
@@ -43,7 +48,7 @@ pub static SPI_CCS_SHARED: Mutex<ThreadModeRawMutex, Option<Output<'static>>> =
 const STICK_HYST_VAL: f32 = 0.3;
 pub const FLOAT_ORIGIN: f32 = 127.5;
 
-#[derive(Clone, Debug, Default, Format)]
+#[derive(Clone, Copy, Debug, Format, PartialEq, Eq)]
 pub struct StickState {
     pub ax: u8,
     pub ay: u8,
@@ -51,6 +56,17 @@ pub struct StickState {
     pub cy: u8,
 }
 
+impl Default for StickState {
+    fn default() -> Self {
+        Self {
+            ax: 127,
+            ay: 127,
+            cx: 127,
+            cy: 127,
+        }
+    }
+}
+
 #[derive(Clone, Debug, Default)]
 struct StickPositions {
     x: f32,
@@ -69,6 +85,51 @@ struct RawStickValues {
     c_unfiltered: XyValuePair<f32>,
 }
 
+#[derive(Clone, Copy, Debug, Default, Format, PartialEq, Eq)]
+pub struct ControllerState {
+    pub button_a: bool,
+    pub button_b: bool,
+    pub button_x: bool,
+    pub button_y: bool,
+    pub trigger_zr: bool,
+    pub trigger_zl: bool,
+    pub trigger_l: bool,
+    pub trigger_r: bool,
+    pub button_start: bool,
+    pub dpad_up: bool,
+    pub dpad_down: bool,
+    pub dpad_left: bool,
+    pub dpad_right: bool,
+    pub stick_state: StickState,
+}
+
+/// This is only implemented for backwards-compatibility purposes
+impl From<GcState> for ControllerState {
+    fn from(value: GcState) -> Self {
+        Self {
+            button_a: value.buttons_1.button_a,
+            button_b: value.buttons_1.button_b,
+            button_x: value.buttons_1.button_x,
+            button_y: value.buttons_1.button_y,
+            trigger_zr: value.buttons_2.button_z,
+            trigger_zl: false,
+            trigger_l: value.trigger_l > 170,
+            trigger_r: value.trigger_r > 170,
+            button_start: value.buttons_2.button_start,
+            dpad_up: value.buttons_1.dpad_up,
+            dpad_down: value.buttons_1.dpad_down,
+            dpad_left: value.buttons_1.dpad_left,
+            dpad_right: value.buttons_1.dpad_right,
+            stick_state: StickState {
+                ax: value.stick_x,
+                ay: value.stick_y,
+                cx: value.stick_x,
+                cy: value.stick_y,
+            },
+        }
+    }
+}
+
 #[derive(PartialEq, Eq, Debug, Clone, Format, Copy)]
 pub enum Stick {
     ControlStick,
@@ -312,7 +373,7 @@ async fn update_stick_states(
     raw_stick_values.c_unfiltered.x = fminf(125., fmaxf(-125., remapped_c_unfiltered.x));
     raw_stick_values.c_unfiltered.y = fminf(125., fmaxf(-125., remapped_c_unfiltered.y));
 
-    let mut out_stick_state = current_stick_state.clone();
+    let mut out_stick_state = *current_stick_state;
 
     let diff_x = (remapped.x + FLOAT_ORIGIN) - current_stick_state.ax as f32;
     if !(-STICK_HYST_VAL..=(1.0 + STICK_HYST_VAL)).contains(&diff_x) {
@@ -345,7 +406,7 @@ async fn update_stick_states(
 
 #[allow(clippy::too_many_arguments)]
 fn update_button_states(
-    gcc_state: &mut GcState,
+    controller_state: &mut ControllerState,
     btn_a: &Input<'_>,
     btn_b: &Input<'_>,
     btn_x: &Input<'_>,
@@ -353,42 +414,37 @@ fn update_button_states(
     btn_start: &Input<'_>,
     btn_l: &Input<'_>,
     btn_r: &Input<'_>,
-    btn_z: &Input<'_>,
+    btn_zr: &Input<'_>,
+    btn_zl: &Input<'_>,
     btn_dleft: &Input<'_>,
     btn_dright: &Input<'_>,
     btn_dup: &Input<'_>,
     btn_ddown: &Input<'_>,
 ) {
-    gcc_state.buttons_1.button_a = btn_a.is_low();
-    gcc_state.buttons_1.button_b = btn_b.is_low();
-    gcc_state.buttons_1.button_x = btn_x.is_low();
-    gcc_state.buttons_1.button_y = btn_y.is_low();
-    gcc_state.buttons_2.button_z = btn_z.is_low();
-    gcc_state.buttons_2.button_start = btn_start.is_low();
-    gcc_state.buttons_2.button_l = btn_l.is_low();
-    gcc_state.buttons_2.button_r = btn_r.is_low();
-    gcc_state.buttons_1.dpad_left = btn_dleft.is_low();
-    gcc_state.buttons_1.dpad_right = btn_dright.is_low();
-    gcc_state.buttons_1.dpad_up = btn_dup.is_low();
-    gcc_state.buttons_1.dpad_down = btn_ddown.is_low();
-    gcc_state.trigger_l = match gcc_state.buttons_2.button_l {
-        true => 255,
-        false => 0,
-    };
-    gcc_state.trigger_r = match gcc_state.buttons_2.button_r {
-        true => 255,
-        false => 0,
-    };
+    controller_state.button_a = btn_a.is_low();
+    controller_state.button_b = btn_b.is_low();
+    controller_state.button_x = btn_x.is_low();
+    controller_state.button_y = btn_y.is_low();
+    controller_state.trigger_zr = btn_zr.is_low();
+    controller_state.trigger_zl = btn_zl.is_low();
+    controller_state.button_start = btn_start.is_low();
+    controller_state.trigger_l = btn_l.is_low();
+    controller_state.trigger_r = btn_r.is_low();
+    controller_state.dpad_left = btn_dleft.is_low();
+    controller_state.dpad_right = btn_dright.is_low();
+    controller_state.dpad_up = btn_dup.is_low();
+    controller_state.dpad_down = btn_ddown.is_low();
 }
 
 #[embassy_executor::task]
 pub async fn input_integrity_benchmark() {
     loop {
-        SIGNAL_OVERRIDE_GCC_STATE.signal(OverrideGcReportInstruction {
+        SIGNAL_OVERRIDE_CONTROLLER_STATE.signal(OverrideGcReportInstruction {
             report: {
-                let mut report = GcState::default();
-                report.buttons_1.dpad_up = true;
-                report
+                ControllerState {
+                    dpad_up: true,
+                    ..Default::default()
+                }
             },
             duration_ms: 100,
         });
@@ -402,7 +458,8 @@ pub async fn input_integrity_benchmark() {
 #[allow(clippy::too_many_arguments)]
 #[embassy_executor::task]
 pub async fn update_button_state_task(
-    btn_z: Input<'static>,
+    btn_zr: Input<'static>,
+    btn_zl: Input<'static>,
     btn_a: Input<'static>,
     btn_b: Input<'static>,
     btn_dright: Input<'static>,
@@ -442,11 +499,11 @@ pub async fn update_button_state_task(
         MUTEX_INPUT_CONSISTENCY_MODE.lock().await.unwrap()
     };
 
-    let mut previous_state = GcState::default();
+    let mut previous_state = ControllerState::default();
 
-    let mut gcc_state = GcState::default();
+    let mut controller_state = ControllerState::default();
 
-    let gcc_publisher = CHANNEL_GCC_STATE.publisher().unwrap();
+    let gcc_publisher = CHANNEL_CONTROLLER_STATE.publisher().unwrap();
 
     let mut override_stick_state: Option<OverrideStickState> = None;
 
@@ -459,7 +516,7 @@ pub async fn update_button_state_task(
 
     loop {
         update_button_states(
-            &mut gcc_state,
+            &mut controller_state,
             &btn_a,
             &btn_b,
             &btn_x,
@@ -467,7 +524,8 @@ pub async fn update_button_state_task(
             &btn_start,
             &btn_l,
             &btn_r,
-            &btn_z,
+            &btn_zr,
+            &btn_zl,
             &btn_dleft,
             &btn_dright,
             &btn_dup,
@@ -476,10 +534,7 @@ pub async fn update_button_state_task(
 
         // not every loop pass is going to update the stick state
         if let Some(stick_state) = SIGNAL_STICK_STATE.try_take() {
-            gcc_state.stick_x = stick_state.ax;
-            gcc_state.stick_y = stick_state.ay;
-            gcc_state.cstick_x = stick_state.cx;
-            gcc_state.cstick_y = stick_state.cy;
+            controller_state.stick_state = stick_state
         }
 
         if let Some(override_stick_state_opt) = SIGNAL_OVERRIDE_STICK_STATE.try_take() {
@@ -488,7 +543,7 @@ pub async fn update_button_state_task(
         }
 
         // check for a gcc state override (usually coming from the config task)
-        if let Some(override_gcc_state) = SIGNAL_OVERRIDE_GCC_STATE.try_take() {
+        if let Some(override_gcc_state) = SIGNAL_OVERRIDE_CONTROLLER_STATE.try_take() {
             trace!("Overridden gcc state: {:?}", override_gcc_state.report);
             let end_time = Instant::now() + Duration::from_millis(override_gcc_state.duration_ms);
             while Instant::now() < end_time {
@@ -506,20 +561,20 @@ pub async fn update_button_state_task(
         };
 
         if let Some(override_state) = &override_stick_state {
-            let mut overriden_gcc_state = gcc_state;
+            let mut overriden_gcc_state = controller_state;
             match override_state.which_stick {
                 Stick::ControlStick => {
-                    overriden_gcc_state.stick_x = override_state.x;
-                    overriden_gcc_state.stick_y = override_state.y;
+                    overriden_gcc_state.stick_state.ax = override_state.x;
+                    overriden_gcc_state.stick_state.ay = override_state.y;
                 }
                 Stick::CStick => {
-                    overriden_gcc_state.cstick_x = override_state.x;
-                    overriden_gcc_state.cstick_y = override_state.y;
+                    overriden_gcc_state.stick_state.cx = override_state.x;
+                    overriden_gcc_state.stick_state.cy = override_state.y;
                 }
             }
             gcc_publisher.publish_immediate(overriden_gcc_state);
         } else {
-            input_filter.apply_filter(&mut gcc_state);
+            input_filter.apply_filter(&mut controller_state);
             if input_consistency_mode == InputConsistencyMode::SuperHack {
                 // transmit state always for the first 5 seconds to give the console time to initialize the controller
                 if initializing && Instant::now().duration_since(init_time) > Duration::from_secs(5)
@@ -527,12 +582,12 @@ pub async fn update_button_state_task(
                     initializing = false;
                 }
 
-                if gcc_state != previous_state || initializing {
-                    gcc_publisher.publish_immediate(gcc_state);
-                    previous_state = gcc_state;
+                if controller_state != previous_state || initializing {
+                    gcc_publisher.publish_immediate(controller_state);
+                    previous_state = controller_state;
                 }
             } else {
-                gcc_publisher.publish_immediate(gcc_state);
+                gcc_publisher.publish_immediate(controller_state);
             }
         }
 
@@ -621,7 +676,7 @@ pub async fn update_stick_states_task(
             last_loop_time = n;
         };
 
-        SIGNAL_STICK_STATE.signal(current_stick_state.clone());
+        SIGNAL_STICK_STATE.signal(current_stick_state);
 
         yield_now().await;
         ticker.next().await;
diff --git a/src/input_filter.rs b/src/input_filter.rs
index 3058a94..a3be235 100644
--- a/src/input_filter.rs
+++ b/src/input_filter.rs
@@ -2,7 +2,7 @@ use defmt::warn;
 
 use crate::{
     config::{is_awaitable_button_pressed, AwaitableButtons},
-    hid::gcc::GcState,
+    input::ControllerState,
 };
 
 /**
@@ -14,7 +14,7 @@ use crate::{
  */
 
 pub trait InputFilter: Sized {
-    fn apply_filter(&mut self, gcc_state: &mut GcState);
+    fn apply_filter(&mut self, controller_state: &mut ControllerState);
 }
 
 /// Presses a single button if another button is pressed.
@@ -26,46 +26,44 @@ pub struct SingleButtonMacroFilter {
 }
 
 impl InputFilter for SingleButtonMacroFilter {
-    fn apply_filter(&mut self, gcc_state: &mut GcState) {
-        if is_awaitable_button_pressed(gcc_state, &self.btn_instigator) {
+    fn apply_filter(&mut self, controller_state: &mut ControllerState) {
+        if is_awaitable_button_pressed(controller_state, &self.btn_instigator) {
             match self.btn_to_press {
                 AwaitableButtons::A => {
-                    gcc_state.buttons_1.button_a = true;
+                    controller_state.button_a = true;
                 }
                 AwaitableButtons::B => {
-                    gcc_state.buttons_1.button_b = true;
+                    controller_state.button_b = true;
                 }
                 AwaitableButtons::X => {
-                    gcc_state.buttons_1.button_x = true;
+                    controller_state.button_x = true;
                 }
                 AwaitableButtons::Y => {
-                    gcc_state.buttons_1.button_y = true;
+                    controller_state.button_y = true;
                 }
                 AwaitableButtons::L => {
-                    gcc_state.trigger_l = 255;
-                    gcc_state.buttons_2.button_l = true;
+                    controller_state.trigger_l = true;
                 }
                 AwaitableButtons::R => {
-                    gcc_state.trigger_r = 255;
-                    gcc_state.buttons_2.button_r = true;
+                    controller_state.trigger_r = true;
                 }
                 AwaitableButtons::Z => {
-                    gcc_state.buttons_2.button_z = true;
+                    controller_state.trigger_zr = true;
                 }
                 AwaitableButtons::Start => {
-                    gcc_state.buttons_2.button_start = true;
+                    controller_state.button_start = true;
                 }
                 AwaitableButtons::Up => {
-                    gcc_state.buttons_1.dpad_up = true;
+                    controller_state.dpad_up = true;
                 }
                 AwaitableButtons::Down => {
-                    gcc_state.buttons_1.dpad_down = true;
+                    controller_state.dpad_down = true;
                 }
                 AwaitableButtons::Left => {
-                    gcc_state.buttons_1.dpad_left = true;
+                    controller_state.dpad_left = true;
                 }
                 AwaitableButtons::Right => {
-                    gcc_state.buttons_1.dpad_right = true;
+                    controller_state.dpad_right = true;
                 }
                 b => {
                     warn!(
@@ -83,22 +81,22 @@ impl InputFilter for SingleButtonMacroFilter {
 pub struct CStickUpTiltFilter;
 
 impl InputFilter for CStickUpTiltFilter {
-    fn apply_filter(&mut self, gcc_state: &mut GcState) {
-        if gcc_state.cstick_y > 157 {
-            if (137..=201).contains(&gcc_state.cstick_x) {
-                gcc_state.cstick_x = 201;
-                gcc_state.cstick_y = 255;
-            } else if (53..=117).contains(&gcc_state.cstick_x) {
-                gcc_state.cstick_x = 53;
-                gcc_state.cstick_y = 255;
+    fn apply_filter(&mut self, controller_state: &mut ControllerState) {
+        if controller_state.stick_state.cy > 157 {
+            if (137..=201).contains(&controller_state.stick_state.cx) {
+                controller_state.stick_state.cx = 201;
+                controller_state.stick_state.cy = 255;
+            } else if (53..=117).contains(&controller_state.stick_state.cx) {
+                controller_state.stick_state.cx = 53;
+                controller_state.stick_state.cy = 255;
             }
-        } else if gcc_state.cstick_y < 97 {
-            if (137..=221).contains(&gcc_state.cstick_x) {
-                gcc_state.cstick_x = 221;
-                gcc_state.cstick_y = 0;
-            } else if (53..=117).contains(&gcc_state.cstick_x) {
-                gcc_state.cstick_x = 33;
-                gcc_state.cstick_y = 0;
+        } else if controller_state.stick_state.cy < 97 {
+            if (137..=221).contains(&controller_state.stick_state.cx) {
+                controller_state.stick_state.cx = 221;
+                controller_state.stick_state.cy = 0;
+            } else if (53..=117).contains(&controller_state.stick_state.cx) {
+                controller_state.stick_state.cx = 33;
+                controller_state.stick_state.cy = 0;
             }
         }
     }
@@ -110,22 +108,22 @@ impl InputFilter for CStickUpTiltFilter {
 pub struct CStickAngledFTiltFilter;
 
 impl InputFilter for CStickAngledFTiltFilter {
-    fn apply_filter(&mut self, gcc_state: &mut GcState) {
-        if gcc_state.cstick_y > 147 {
-            if (147..=225).contains(&gcc_state.cstick_x) {
-                gcc_state.cstick_x = 205;
-                gcc_state.cstick_y = 205;
-            } else if (30..=107).contains(&gcc_state.cstick_x) {
-                gcc_state.cstick_x = 50;
-                gcc_state.cstick_y = 205;
+    fn apply_filter(&mut self, controller_state: &mut ControllerState) {
+        if controller_state.stick_state.cy > 147 {
+            if (147..=225).contains(&controller_state.stick_state.cx) {
+                controller_state.stick_state.cx = 205;
+                controller_state.stick_state.cy = 205;
+            } else if (30..=107).contains(&controller_state.stick_state.cx) {
+                controller_state.stick_state.cx = 50;
+                controller_state.stick_state.cy = 205;
             }
-        } else if gcc_state.cstick_y < 107 {
-            if (147..=225).contains(&gcc_state.cstick_x) {
-                gcc_state.cstick_x = 205;
-                gcc_state.cstick_y = 50;
-            } else if (30..=107).contains(&gcc_state.cstick_x) {
-                gcc_state.cstick_x = 50;
-                gcc_state.cstick_y = 50;
+        } else if controller_state.stick_state.cy < 107 {
+            if (147..=225).contains(&controller_state.stick_state.cx) {
+                controller_state.stick_state.cx = 205;
+                controller_state.stick_state.cy = 50;
+            } else if (30..=107).contains(&controller_state.stick_state.cx) {
+                controller_state.stick_state.cx = 50;
+                controller_state.stick_state.cy = 50;
             }
         }
     }
@@ -136,5 +134,5 @@ impl InputFilter for CStickAngledFTiltFilter {
 pub struct DummyFilter;
 
 impl InputFilter for DummyFilter {
-    fn apply_filter(&mut self, _gcc_state: &mut GcState) {}
+    fn apply_filter(&mut self, _gcc_state: &mut ControllerState) {}
 }
diff --git a/src/main.rs b/src/main.rs
index dc97008..6abcc03 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -89,6 +89,7 @@ fn main() -> ! {
                 spawner
                     .spawn(update_button_state_task(
                         Input::new(AnyPin::from(p.PIN_20), gpio::Pull::Up),
+                        Input::new(AnyPin::from(p.PIN_15), gpio::Pull::Up),
                         Input::new(AnyPin::from(p.PIN_17), gpio::Pull::Up),
                         Input::new(AnyPin::from(p.PIN_16), gpio::Pull::Up),
                         Input::new(AnyPin::from(p.PIN_11), gpio::Pull::Up),
diff --git a/src/usb_comms.rs b/src/usb_comms.rs
index e89692d..7a76df1 100644
--- a/src/usb_comms.rs
+++ b/src/usb_comms.rs
@@ -25,7 +25,7 @@ use libm::powf;
 use crate::{
     config::{ControllerMode, InputConsistencyMode},
     hid::{
-        gcc::{GcReportBuilder, GcState, GccRequestHandler, GCC_REPORT_DESCRIPTOR},
+        gcc::{GcReportBuilder, GccRequestHandler, GCC_REPORT_DESCRIPTOR},
         procon::{ProconReportBuilder, ProconRequestHandler, PROCON_REPORT_DESCRIPTOR},
         xinput::{
             XInputReaderWriter, XInputReportBuilder, XInputRequestHandler, XInputState,
@@ -33,7 +33,7 @@ use crate::{
         },
         HidReaderWriterSplit, UsbReader, UsbWriter,
     },
-    input::CHANNEL_GCC_STATE,
+    input::{ControllerState, CHANNEL_CONTROLLER_STATE},
 };
 
 pub static SIGNAL_RUMBLE: Signal<CriticalSectionRawMutex, bool> = Signal::new();
@@ -56,7 +56,7 @@ pub static MUTEX_CONTROLLER_MODE: Mutex<CriticalSectionRawMutex, Option<Controll
 const DEVICE_INTERFACE_GUID: &str = "{ecceff35-146c-4ff3-acd9-8f992d09acdd}";
 
 pub trait HidReportBuilder<const LEN: usize> {
-    async fn get_hid_report(&mut self, state: &GcState) -> [u8; LEN];
+    async fn get_hid_report(&mut self, state: &ControllerState) -> [u8; LEN];
 }
 
 struct UsbConfig {
@@ -216,7 +216,7 @@ where
     };
 
     let in_fut = async move {
-        let mut gcc_subscriber = CHANNEL_GCC_STATE.subscriber().unwrap();
+        let mut gcc_subscriber = CHANNEL_CONTROLLER_STATE.subscriber().unwrap();
 
         let mut last_report_time = Instant::now();
         let mut rate_limit_end_time = Instant::now();