diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index fc2e841..461d247 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -3,6 +3,7 @@ name: CI
 on:
   push:
   pull_request:
+  workflow_dispatch:
   schedule:
     - cron: '0 0 * * 3'
 
@@ -61,7 +62,7 @@ jobs:
           HOMEBREW_NO_AUTO_UPDATE=1 brew install sdl2
 
       - uses: actions/checkout@v2
-      - uses: actions/setup-python@v1
+      - uses: actions/setup-python@v5
 
       - name: Install PlatformIO
         run: |
diff --git a/hal/sdl2/app_hal.c b/hal/sdl2/app_hal.c
index 490dbf2..392e344 100644
--- a/hal/sdl2/app_hal.c
+++ b/hal/sdl2/app_hal.c
@@ -1,29 +1,26 @@
 #include <unistd.h>
 #define SDL_MAIN_HANDLED        /*To fix SDL's "undefined reference to WinMain" issue*/
 #include <SDL2/SDL.h>
-#include "display/monitor.h"
-#include "indev/mouse.h"
-#include "indev/mousewheel.h"
-#include "indev/keyboard.h"
-#include "sdl/sdl.h"
+#include "drivers/sdl/lv_sdl_mouse.h"
+#include "drivers/sdl/lv_sdl_mousewheel.h"
+#include "drivers/sdl/lv_sdl_keyboard.h"
 
 
-/**
- * A task to measure the elapsed time for LittlevGL
- * @param data unused
- * @return never return
- */
-static int tick_thread(void * data)
-{
-    (void)data;
 
-    while(1) {
-        SDL_Delay(5);   /*Sleep for 5 millisecond*/
-        lv_tick_inc(5); /*Tell LittelvGL that 5 milliseconds were elapsed*/
-    }
 
-    return 0;
+static lv_display_t *lvDisplay;
+static lv_indev_t *lvMouse;
+static lv_indev_t *lvMouseWheel;
+static lv_indev_t *lvKeyboard;
+
+
+#if LV_USE_LOG != 0
+static void lv_log_print_g_cb(lv_log_level_t level, const char * buf)
+{
+  LV_UNUSED(level);
+  LV_UNUSED(buf);
 }
+#endif
 
 
 void hal_setup(void)
@@ -34,43 +31,28 @@ void hal_setup(void)
         setenv("DBUS_FATAL_WARNINGS", "0", 1);
     #endif
 
+    #if LV_USE_LOG != 0
+    lv_log_register_print_cb(lv_log_print_g_cb);
+    #endif
+
     /* Add a display
      * Use the 'monitor' driver which creates window on PC's monitor to simulate a display*/
 
-    static lv_disp_draw_buf_t disp_buf;
-    static lv_color_t buf[SDL_HOR_RES * 10];                          /*Declare a buffer for 10 lines*/
-    lv_disp_draw_buf_init(&disp_buf, buf, NULL, SDL_HOR_RES * 10);    /*Initialize the display buffer*/
-
-    static lv_disp_drv_t disp_drv;
-    lv_disp_drv_init(&disp_drv);              /*Basic initialization*/
-    disp_drv.flush_cb = sdl_display_flush;    /*Used when `LV_VDB_SIZE != 0` in lv_conf.h (buffered drawing)*/
-    disp_drv.draw_buf = &disp_buf;
-    disp_drv.hor_res = SDL_HOR_RES;
-    disp_drv.ver_res = SDL_VER_RES;
-    //disp_drv.disp_fill = monitor_fill;      /*Used when `LV_VDB_SIZE == 0` in lv_conf.h (unbuffered drawing)*/
-    //disp_drv.disp_map = monitor_map;        /*Used when `LV_VDB_SIZE == 0` in lv_conf.h (unbuffered drawing)*/
-    lv_disp_drv_register(&disp_drv);
-
-    /* Add the mouse as input device
-     * Use the 'mouse' driver which reads the PC's mouse*/
-    static lv_indev_drv_t indev_drv;
-    lv_indev_drv_init(&indev_drv);            /*Basic initialization*/
-    indev_drv.type = LV_INDEV_TYPE_POINTER;
-    indev_drv.read_cb = sdl_mouse_read;       /*This function will be called periodically (by the library) to get the mouse position and state*/
-    lv_indev_drv_register(&indev_drv);
-
-    sdl_init();
 
-    /* Tick init.
-     * You have to call 'lv_tick_inc()' in periodically to inform LittelvGL about how much time were elapsed
-     * Create an SDL thread to do this*/
-    SDL_CreateThread(tick_thread, "tick", NULL);
+    lvDisplay = lv_sdl_window_create(SDL_HOR_RES, SDL_VER_RES);
+    lvMouse = lv_sdl_mouse_create();
+    lvMouseWheel = lv_sdl_mousewheel_create();
+    lvKeyboard = lv_sdl_keyboard_create();
 }
 
 void hal_loop(void)
 {
+    Uint32 lastTick = SDL_GetTicks();
     while(1) {
         SDL_Delay(5);
-        lv_task_handler();
+        Uint32 current = SDL_GetTicks();
+        lv_tick_inc(current - lastTick); // Update the tick timer. Tick is new for LVGL 9
+        lastTick = current;
+        lv_timer_handler(); // Update the UI-
     }
 }
diff --git a/hal/stm32f429_disco/tft.c b/hal/stm32f429_disco/tft.c
index 07798d2..60360af 100644
--- a/hal/stm32f429_disco/tft.c
+++ b/hal/stm32f429_disco/tft.c
@@ -9,6 +9,7 @@
 #include <string.h>
 
 #include "tft.h"
+#include <lvgl.h>
 #include "stm32f4xx.h"
 #include "stm32f429i_discovery_lcd.h"
 #include "ili9341.h"
@@ -19,6 +20,8 @@
 
 #define SDRAM_BANK_ADDR ((uint32_t)0xD0000000)
 
+#define LV_BUFFER_SIZE  (TFT_HOR_RES * TFT_VER_RES / 8 * (LV_COLOR_DEPTH / 8))
+
 #define DMA_STREAM DMA2_Stream0
 #define DMA_CHANNEL DMA_CHANNEL_0
 #define DMA_STREAM_IRQ DMA2_Stream0_IRQn
@@ -32,7 +35,7 @@
  *  STATIC PROTOTYPES
  **********************/
 
-static void tft_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_p);
+static void tft_flush(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map);
 
 /**********************
  *  STATIC VARIABLES
@@ -55,18 +58,22 @@ static void DMA_TransferComplete(DMA_HandleTypeDef *han);
 static void DMA_TransferError(DMA_HandleTypeDef *han);
 
 DMA_HandleTypeDef DmaHandle;
-static lv_disp_drv_t disp_drv;
+static lv_display_t * lvDisplay;
+
+static uint8_t lvBuffer[LV_BUFFER_SIZE];
 static int32_t x1_flush;
 static int32_t y1_flush;
 static int32_t x2_flush;
 static int32_t y2_fill;
 static int32_t y_fill_act;
-static const lv_color_t *buf_to_flush;
+static const uint8_t *buf_to_flush;
 
 /**********************
  *      MACROS
  **********************/
 
+
+
 /**********************
  *   GLOBAL FUNCTIONS
  **********************/
@@ -75,32 +82,47 @@ static const lv_color_t *buf_to_flush;
  */
 void tft_init(void)
 {
-  static lv_color_t disp_buf1[TFT_HOR_RES * 40];
-  static lv_disp_draw_buf_t buf;
-  lv_disp_draw_buf_init(&buf, disp_buf1, NULL, TFT_HOR_RES * 40);
-
-  lv_disp_drv_init(&disp_drv);
-
-  BSP_LCD_Init();
-  BSP_LCD_LayerDefaultInit(0, (uint32_t)my_fb);
-  HAL_LTDC_SetPixelFormat(&LtdcHandler, LTDC_PIXEL_FORMAT_RGB565, 0);
-  DMA_Config();
-  disp_drv.draw_buf = &buf;
-  disp_drv.flush_cb = tft_flush;
-  disp_drv.hor_res = TFT_HOR_RES;
-  disp_drv.ver_res = TFT_VER_RES;
-#if TFT_USE_GPU != 0
-  DMA2D_Config();
-  disp_drv.gpu_blend_cb = gpu_mem_blend;
-  disp_drv.gpu_fill_cb = gpu_mem_fill;
-#endif
-  lv_disp_drv_register(&disp_drv);
+  #if LV_USE_LOG != 0
+    lv_log_register_print_cb(lv_log_print_g_cb);
+  #endif
+
+  lvDisplay = lv_display_create(TFT_HOR_RES, TFT_VER_RES);
+  if (lvDisplay)
+  {
+    BSP_LCD_Init();
+    BSP_LCD_LayerDefaultInit(0, (uint32_t)my_fb);
+    HAL_LTDC_SetPixelFormat(&LtdcHandler, LTDC_PIXEL_FORMAT_RGB565, 0);
+    DMA_Config();
+
+    lv_display_set_color_format(lvDisplay, LV_COLOR_FORMAT_RGB565);
+    lv_display_set_flush_cb(lvDisplay, tft_flush);
+    lv_display_set_buffers(lvDisplay, lvBuffer, NULL, LV_BUFFER_SIZE, LV_DISPLAY_RENDER_MODE_PARTIAL);
+    
+  #if TFT_USE_GPU != 0
+    DMA2D_Config();
+    disp_drv.gpu_blend_cb = gpu_mem_blend;
+    disp_drv.gpu_fill_cb = gpu_mem_fill;
+  #endif
+  }
 }
 
 /**********************
  *   STATIC FUNCTIONS
  **********************/
 
+/**
+ * LVGL Log print callback
+ * @param level log message level
+ * @param buf string to print 
+ */
+#if LV_USE_LOG != 0
+static void lv_log_print_g_cb(lv_log_level_t level, const char * buf)
+{
+  LV_UNUSED(level);
+  LV_UNUSED(buf);
+}
+#endif
+
 /**
  * Flush a color buffer
  * @param x1 left coordinate of the rectangle
@@ -109,7 +131,7 @@ void tft_init(void)
  * @param y2 bottom coordinate of the rectangle
  * @param color_p pointer to an array of colors
  */
-static void tft_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_p)
+static void tft_flush(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map)
 {
   /*Return if the area is out the screen*/
   if (area->x2 < 0)
@@ -132,7 +154,7 @@ static void tft_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *col
   x2_flush = act_x2;
   y2_fill = act_y2;
   y_fill_act = act_y1;
-  buf_to_flush = color_p;
+  buf_to_flush = px_map;
 
   /*##-7- Start the DMA transfer using the interrupt mode #*/
   /* Configure the source, destination and buffer size DMA fields and Start DMA Stream transfer */
@@ -197,7 +219,7 @@ static void DMA_TransferComplete(DMA_HandleTypeDef *han)
 
   if (y_fill_act > y2_fill)
   {
-    lv_disp_flush_ready(&disp_drv);
+    lv_disp_flush_ready(lvDisplay);
   }
   else
   {
diff --git a/hal/stm32f429_disco/touchpad.c b/hal/stm32f429_disco/touchpad.c
index b16d581..7aeae08 100644
--- a/hal/stm32f429_disco/touchpad.c
+++ b/hal/stm32f429_disco/touchpad.c
@@ -24,7 +24,7 @@
 /**********************
  *  STATIC PROTOTYPES
  **********************/
-static void touchpad_read(lv_indev_drv_t * drv, lv_indev_data_t *data);
+static void touchpad_read(lv_indev_t * drv, lv_indev_data_t *data);
 static bool touchpad_get_xy(int16_t *x, int16_t *y);
 
 /**********************
@@ -47,11 +47,9 @@ void touchpad_init(void)
   stmpe811_Init(TS_I2C_ADDRESS);
   stmpe811_TS_Start(TS_I2C_ADDRESS);
 
-  lv_indev_drv_t indev_drv;
-  lv_indev_drv_init(&indev_drv);
-  indev_drv.read_cb = touchpad_read;
-  indev_drv.type = LV_INDEV_TYPE_POINTER;
-  lv_indev_drv_register(&indev_drv);
+  lv_indev_t  * indev_drv = lv_indev_create();
+  lv_indev_set_read_cb(indev_drv, touchpad_read);
+  lv_indev_set_type(indev_drv, LV_INDEV_TYPE_POINTER);
 }
 
 /**********************
@@ -65,7 +63,7 @@ void touchpad_init(void)
  * @param y put the y coordinate here
  * @return true: the device is pressed, false: released
  */
-static void touchpad_read(lv_indev_drv_t * drv, lv_indev_data_t *data)
+static void touchpad_read(lv_indev_t * dev, lv_indev_data_t *data)
 {
 	static int16_t last_x = 0;
 	static int16_t last_y = 0;
diff --git a/platformio.ini b/platformio.ini
index e515483..153fee1 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -22,14 +22,15 @@ build_flags =
   ; Add more defines below to overide lvgl:/src/lv_conf_simple.h
 lib_deps =
   ; Use direct URL, because package registry is unstable
-  ;lvgl@~7.11.0
-  lvgl=https://github.com/lvgl/lvgl/archive/refs/tags/v8.2.0.zip
+  lvgl@9.1
 lib_archive = false
 
 
 [env:emulator_64bits]
 platform = native@^1.1.3
-extra_scripts = support/sdl2_build_extra.py
+extra_scripts = 
+  pre:support/sdl2_paths.py ; Tries to find SDL2 include and lib paths on your system - specifically for MacOS w/ Homebrew
+  post:support/sdl2_build_extra.py
 build_flags =
   ${env.build_flags}
   ; -D LV_LOG_LEVEL=LV_LOG_LEVEL_INFO
@@ -40,25 +41,18 @@ build_flags =
   ; SDL drivers options
   -D LV_LVGL_H_INCLUDE_SIMPLE
   -D LV_DRV_NO_CONF
-  -D USE_SDL
+  -D LV_USE_SDL
   -D SDL_HOR_RES=480
   -D SDL_VER_RES=320  
   -D SDL_ZOOM=1
-  -D SDL_INCLUDE_PATH="\"SDL2/SDL.h\""
+  -D LV_SDL_INCLUDE_PATH="\"SDL2/SDL.h\""
 
   ; LVGL memory options, setup for the demo to run properly
   -D LV_MEM_CUSTOM=1
   -D LV_MEM_SIZE="(128U * 1024U)"
-
-  ; SDL2 includes, uncomment the next two lines on MAC OS if you intalled sdl via homebrew
-  ;  !find /opt/homebrew/Cellar/sdl2 -name "include" | sed "s/^/-I /"
-  ;  !find /opt/homebrew/Cellar/sdl2 -name "libSDL2.a" | xargs dirname | sed "s/^/-L /"
   
 lib_deps =
   ${env.lib_deps}
-  ; Use direct URL, because package registry is unstable
-  ;lv_drivers@~7.9.0
-  lv_drivers=https://github.com/lvgl/lv_drivers/archive/refs/tags/v8.2.0.zip
 build_src_filter =
   +<*>
   +<../hal/sdl2>
diff --git a/support/sdl2_paths.py b/support/sdl2_paths.py
new file mode 100644
index 0000000..be6aa94
--- /dev/null
+++ b/support/sdl2_paths.py
@@ -0,0 +1,27 @@
+Import("env")
+import sys
+import os
+import glob
+
+if sys.platform.startswith("darwin"):
+    #sdl_include_path = !find /opt/homebrew/Cellar/sdl2 -name "include" | sed "s/^/-I /"
+    sdl_include = glob.glob("/opt/homebrew/Cellar/sdl2/*/include", recursive=True)
+    if sdl_include:
+        print(f"Found Homebrew SDL include path: {sdl_include[0]}")
+        env.Append(
+            CPPPATH=sdl_include[0]
+        )
+    sdl_lib = glob.glob("/opt/homebrew/Cellar/sdl2/**/libSDL2.a", recursive=True)
+    if sdl_lib:
+        print(f"Found Homebrew SDL lib path: {sdl_lib[0]}")
+        env.Append(
+            LIBPATH=os.path.dirname(sdl_lib[0])
+        )
+    
+    
+#breakpoint()
+    
+#print('NewENV=====================================')
+#print(env.Dump())
+#print('DefaultENV=====================================')
+#print(DefaultEnvironment().Dump())