diff --git a/apps/gui/bitmap/list.c b/apps/gui/bitmap/list.c
index 6ce8972..da4f570 100644
--- a/apps/gui/bitmap/list.c
+++ b/apps/gui/bitmap/list.c
@@ -53,6 +53,9 @@ static enum {
     SCROLL_BAR,     /* scroll by using the scrollbar */
     SCROLL_SWIPE,   /* scroll by wiping over the screen */
 } scroll_mode;
+
+static int y_offset;
+static int last_nb_items;
 #endif

 int gui_list_get_item_offset(struct gui_synclist * gui_list, int item_width,
@@ -146,6 +149,13 @@ void list_draw(struct screen *display, struct gui_synclist *list)
     bool show_title;
     struct viewport *list_text_vp = &list_text[screen];

+    /* hack! */
+    if (last_nb_items != list->nb_items)
+    {
+        y_offset = 0;
+        last_nb_items = list->nb_items;
+    }
+
     line_height = font_get(parent->font)->height;
     display->set_viewport(parent);
     display->clear_viewport();
@@ -157,8 +167,17 @@ void list_draw(struct screen *display, struct gui_synclist *list)
         list_text_vp->height -= line_height;
     }

+    int y_off = y_offset;
     start = list_start_item;
     end = start + viewport_get_nb_lines(list_text_vp);
+    if (y_offset > 0)
+    {
+        y_off -= line_height; /* make it negative */
+        if (start > 0)
+            start--;
+    }
+    if (y_offset < 0)
+        end++;

     /* draw the scrollbar if its needed */
     if (global_settings.scrollbar &&
@@ -216,6 +235,7 @@ void list_draw(struct screen *display, struct gui_synclist *list)
         char entry_buffer[MAX_PATH];
         unsigned char *entry_name;
         int text_pos = 0;
+        int line = i - start;
         s = list->callback_get_item_name(i, list->data, entry_buffer,
                                          sizeof(entry_buffer));
         entry_name = P2STR(s);
@@ -285,36 +305,36 @@ void list_draw(struct screen *display, struct gui_synclist *list)
             if (item_offset> item_width - (list_text_vp->width - text_pos))
             {
                 /* don't scroll */
-                display->puts_style_offset(0, i-start, entry_name,
-                        style, item_offset);
+                display->puts_style_xyoffset(0, line, entry_name,
+                        style, item_offset, y_off);
             }
             else
             {
-                display->puts_scroll_style_offset(0, i-start, entry_name,
-                        style, item_offset);
+                display->puts_scroll_style_xyoffset(0, line, entry_name,
+                        style, item_offset, y_off);
             }
         }
         else
         {
             if (list->scroll_all)
-                display->puts_scroll_style_offset(0, i-start, entry_name,
-                        style, item_offset);
+                display->puts_scroll_style_xyoffset(0, line, entry_name,
+                        style, item_offset, y_off);
             else
-                display->puts_style_offset(0, i-start, entry_name,
-                        style, item_offset);
+                display->puts_style_xyoffset(0, line, entry_name,
+                        style, item_offset, y_off);
         }
         /* do the icon */
         display->set_viewport(&list_icons);
         if (list->callback_get_item_icon && global_settings.show_icons)
         {
             screen_put_icon_with_offset(display, show_cursor?1:0,
-                                    (i-start),show_cursor?ICON_PADDING:0,0,
+                                    (line),show_cursor?ICON_PADDING:0,y_off,
                                     list->callback_get_item_icon(i, list->data));
         }
         if (show_cursor && i >= list->selected_item &&
                 i <  list->selected_item + list->selected_size)
         {
-            screen_put_icon(display, 0, i-start, Icon_Cursor);
+            screen_put_icon_with_offset(display, 0, line, 0, y_off, Icon_Cursor);
         }
     }
     display->set_viewport(parent);
@@ -343,6 +363,8 @@ static int gui_synclist_touchscreen_scrollbar(struct gui_synclist * gui_list,
     if (nb_lines <  gui_list->nb_items)
     {
         scroll_mode = SCROLL_BAR;
+        /* scrollbar scrolling is still line based */
+        y_offset = 0;
         int scrollbar_size = nb_lines*
             font_get(gui_list->parent[screen]->font)->height;
         int actual_y = y - list_text[screen].y;
@@ -364,42 +386,76 @@ static int gui_synclist_touchscreen_scrollbar(struct gui_synclist * gui_list,
     return ACTION_NONE;
 }

+#define SIGN(x) ((x) < 0 ? -1 : 1)
 /*
- * returns the number of scrolled items since the last call
+ * returns the number of pixel scrolled since the last call
  **/
-static int gui_synclist_touchscreen_scrolling(struct gui_synclist * gui_list, int position)
+static int gui_synclist_touchscreen_scrolling(struct gui_synclist * gui_list, int line_height, int position)
 {
-    const int screen = screens[SCREEN_MAIN].screen_type;
-    const int difference = position - last_position;
+    /* fixme */
+    const enum screen_type screen = screens[SCREEN_MAIN].screen_type;
     const int nb_lines = viewport_get_nb_lines(&list_text[screen]);
-    if(nb_lines < gui_list->nb_items && difference != 0) /* only scroll if needed */
+    /* in pixels */
+    const int difference = position - last_position;
+
+    /* make selecting items easier */
+    if (abs(difference) < 3 && scroll_mode == SCROLL_NONE)
+        return 0;
+
+    /* does the list even scroll? if no, return but still show
+     * the caller that we would scroll */
+    if (nb_lines >= gui_list->nb_items)
+        return difference;
+
+    const int old_start = gui_list->start_item[screen];
+    int new_start_item = -1;
+    int line_diff = 0;
+
+    /* don't scroll at the edges of the list */
+    if ((old_start == 0 && difference > 0)
+     || (old_start == (gui_list->nb_items - nb_lines) && difference < 0))
+    {
+        y_offset = 0;
+        return difference;
+    }
+
+    /* add up y_offset over time and translate to lines
+     * if scrolled enough */
+    y_offset += difference;
+    if (abs(y_offset) > line_height)
     {
-        int new_start_item;
-        new_start_item = gui_list->start_item[screen] - difference;
+        line_diff = y_offset/line_height;
+        y_offset -= line_diff * line_height;
+    }
+
+    if(line_diff != 0)
+    {
+        new_start_item = old_start - line_diff;
         /* check if new_start_item is bigger than list item count */
         if(new_start_item > gui_list->nb_items - nb_lines)
             new_start_item = gui_list->nb_items - nb_lines;
         /* set new_start_item to 0 if it's negative */
-        if(new_start_item < 0)
+        if(new_start_item <= 0)
             new_start_item = 0;
         gui_list->start_item[screen] = new_start_item;
     }
+
     return difference;
 }

 unsigned gui_synclist_do_touchscreen(struct gui_synclist * gui_list)
 {
     short x, y;
-    const int button = action_get_touchscreen_press(&x, &y);
-    const int screen = SCREEN_MAIN;
+    const enum screen_type screen = SCREEN_MAIN;
+    struct viewport *info_vp = sb_skin_get_info_vp(screen);
+    const int button = action_get_touchscreen_press_in_vp(&x, &y, info_vp);
     const int list_start_item = gui_list->start_item[screen];
     const struct viewport *list_text_vp = &list_text[screen];
     const bool old_released = released;
     int line, list_width = list_text_vp->width;

-    /* make sure it is inside the UI viewport */    
-    if (!viewport_point_within_vp(sb_skin_get_info_vp(screen), x, y))
-        return BUTTON_NONE;
+    if (button == ACTION_NONE || button == ACTION_UNKNOWN)
+        return ACTION_NONE;

     released = (button&BUTTON_REL) != 0;

@@ -478,10 +534,6 @@ unsigned gui_synclist_do_touchscreen(struct gui_synclist * gui_list)
             /* Pressed below the list*/
             if (list_start_item + line >= gui_list->nb_items)
                 return ACTION_NONE;
-            
-            /* Pressed a border */
-            if(UNLIKELY(actual_y % line_height == 0))
-                return ACTION_NONE;

             if (released)
             {
@@ -524,9 +576,9 @@ unsigned gui_synclist_do_touchscreen(struct gui_synclist * gui_list)
                 /* select current item */
                 gui_synclist_select_item(gui_list, list_start_item+line);
                 if (last_position == 0)
-                    last_position = line;
+                    last_position = actual_y;
                 else
-                    result = gui_synclist_touchscreen_scrolling(gui_list, line);
+                    result = gui_synclist_touchscreen_scrolling(gui_list, line_height, actual_y);

                 /* Start scrolling once the pen is moved without
                  * releasing it inbetween */
@@ -536,7 +588,7 @@ unsigned gui_synclist_do_touchscreen(struct gui_synclist * gui_list)
                     scroll_mode = SCROLL_SWIPE;
                 }

-                last_position = line;
+                last_position = actual_y;

                 return redraw ? ACTION_REDRAW:ACTION_NONE;
             }
diff --git a/apps/screen_access.c b/apps/screen_access.c
index 7b64c40..d059547 100644
--- a/apps/screen_access.c
+++ b/apps/screen_access.c
@@ -168,8 +168,10 @@ struct screen screens[NB_SCREENS] =
         .hline=&lcd_hline,
         .scroll_step=&lcd_scroll_step,
         .puts_style_offset=&lcd_puts_style_offset,
+        .puts_style_xyoffset=&lcd_puts_style_xyoffset,
         .puts_scroll_style=&lcd_puts_scroll_style,
         .puts_scroll_style_offset=&lcd_puts_scroll_style_offset,
+        .puts_scroll_style_xyoffset=&lcd_puts_scroll_style_xyoffset,
 #endif /* HAVE_LCD_BITMAP */

 #ifdef HAVE_LCD_CHARCELLS
@@ -257,8 +259,10 @@ struct screen screens[NB_SCREENS] =
         .hline=&lcd_remote_hline,
         .scroll_step=&lcd_remote_scroll_step,
         .puts_style_offset=&lcd_remote_puts_style_offset,
+        .puts_style_xyoffset=&lcd_remote_puts_style_xyoffset,
         .puts_scroll_style=&lcd_remote_puts_scroll_style,
         .puts_scroll_style_offset=&lcd_remote_puts_scroll_style_offset,
+        .puts_scroll_style_xyoffset=&lcd_remote_puts_scroll_style_xyoffset,
 #endif /* 1 */

 #if 0 /* no charcell remote LCDs so far */
diff --git a/apps/screen_access.h b/apps/screen_access.h
index 463ca3f..a154d20 100644
--- a/apps/screen_access.h
+++ b/apps/screen_access.h
@@ -77,11 +77,15 @@ struct screen

     void (*scroll_step)(int pixels);
     void (*puts_style_offset)(int x, int y, const unsigned char *str,
-                              int style, int offset);
+                              int style, int x_offset);
+    void (*puts_style_xyoffset)(int x, int y, const unsigned char *str,
+                                int style, int x_offset, int y_offset);
     void (*puts_scroll_style)(int x, int y, const unsigned char *string,
                                  int style);
     void (*puts_scroll_style_offset)(int x, int y, const unsigned char *string,
-                                     int style, int offset);
+                                     int style, int x_offset);
+    void (*puts_scroll_style_xyoffset)(int x, int y, const unsigned char *string,
+                                     int style, int x_offset, int y_offset);
     void (*mono_bitmap)(const unsigned char *src,
                         int x, int y, int width, int height);
     void (*mono_bitmap_part)(const unsigned char *src, int src_x, int src_y,
@@ -134,7 +138,9 @@ struct screen
     void (*puts_offset)(int x, int y, const unsigned char *str, int offset);
     void (*puts_scroll)(int x, int y, const unsigned char *string);
     void (*puts_scroll_offset)(int x, int y, const unsigned char *string,
-                                 int offset);
+                                 int x_offset);
+    void (*puts_scroll_xyoffset)(int x, int y, const unsigned char *string,
+                                 int x_offset, int y_offset);
     void (*scroll_speed)(int speed);
     void (*scroll_delay)(int ms);
     void (*stop_scroll)(void);
diff --git a/firmware/drivers/lcd-bitmap-common.c b/firmware/drivers/lcd-bitmap-common.c
index 9f1d5fe..2ce11e3 100644
--- a/firmware/drivers/lcd-bitmap-common.c
+++ b/firmware/drivers/lcd-bitmap-common.c
@@ -284,8 +284,8 @@ static void LCDFN(putsxyofs_style)(int xpos, int ypos,
 /*** Line oriented text output ***/

 /* put a string at a given char position */
-void LCDFN(puts_style_offset)(int x, int y, const unsigned char *str,
-                              int style, int offset)
+void LCDFN(puts_style_xyoffset)(int x, int y, const unsigned char *str,
+                              int style, int x_offset, int y_offset)
 {
     int xpos, ypos, w, h;
     LCDFN(scroll_stop_line)(current_vp, y);
@@ -294,8 +294,14 @@ void LCDFN(puts_style_offset)(int x, int y, const unsigned char *str,

     LCDFN(getstringsize)(str, &w, &h);
     xpos = x * LCDFN(getstringsize)(" ", NULL, NULL);
-    ypos = y * h;
-    LCDFN(putsxyofs_style)(xpos, ypos, str, style, w, h, offset);
+    ypos = y * h + y_offset;
+    LCDFN(putsxyofs_style)(xpos, ypos, str, style, w, h, x_offset);
+}
+
+void LCDFN(puts_style_offset)(int x, int y, const unsigned char *str,
+                              int style, int x_offset)
+{
+    LCDFN(puts_style_xyoffset)(x, y, str, style, x_offset, 0);
 }

 void LCDFN(puts)(int x, int y, const unsigned char *str)
@@ -326,8 +332,8 @@ void LCDFN(puts_offset)(int x, int y, const unsigned char *str, int offset)

 /*** scrolling ***/

-void LCDFN(puts_scroll_style_offset)(int x, int y, const unsigned char *string,
-                                     int style, int offset)
+void LCDFN(puts_scroll_style_xyoffset)(int x, int y, const unsigned char *string,
+                                     int style, int x_offset, int y_offset)
 {
     struct scrollinfo* s;
     char *end;
@@ -343,7 +349,7 @@ void LCDFN(puts_scroll_style_offset)(int x, int y, const unsigned char *string,
     if (LCDFN(scroll_info).lines >= LCDM(SCROLLABLE_LINES)) return;
     if (!string)
         return;
-    LCDFN(puts_style_offset)(x, y, string, style, offset);
+    LCDFN(puts_style_xyoffset)(x, y, string, style, x_offset, y_offset);

     LCDFN(getstringsize)(string, &w, &h);

@@ -381,8 +387,9 @@ void LCDFN(puts_scroll_style_offset)(int x, int y, const unsigned char *string,

     s->vp = current_vp;
     s->y = y;
-    s->offset = offset;
+    s->offset = x_offset;
     s->startx = x * LCDFN(getstringsize)(" ", NULL, NULL);
+    s->y_offset = y_offset;
     s->backward = false;

     LCDFN(scroll_info).lines++;
@@ -429,7 +436,7 @@ void LCDFN(scroll_fn)(void)

         pf = font_get(current_vp->font);
         xpos = s->startx;
-        ypos = s->y * pf->height;
+        ypos = s->y * pf->height + s->y_offset;

         if (s->bidir) { /* scroll bidirectional */
             if (s->offset <= 0) {
@@ -457,3 +464,9 @@ void LCDFN(scroll_fn)(void)
     }
     LCDFN(set_viewport)(old_vp);
 }
+
+void LCDFN(puts_scroll_style_offset)(int x, int y, const unsigned char *string,
+                                     int style, int x_offset)
+{
+    LCDFN(puts_scroll_style_xyoffset)(x, y, string, style, x_offset, 0);
+}
diff --git a/firmware/export/lcd.h b/firmware/export/lcd.h
index 1cbb286..54a2f3c 100644
--- a/firmware/export/lcd.h
+++ b/firmware/export/lcd.h
@@ -192,6 +192,8 @@ extern void lcd_clear_viewport(void);
 extern void lcd_clear_display(void);
 extern void lcd_putsxy(int x, int y, const unsigned char *string);
 extern void lcd_putsxyf(int x, int y, const unsigned char *fmt, ...);
+extern void lcd_putsxy_style_offset(int x, int y, const unsigned char *str,
+                                    int style, int offset);
 extern void lcd_puts(int x, int y, const unsigned char *string);
 extern void lcd_putsf(int x, int y, const unsigned char *fmt, ...);
 extern void lcd_puts_style(int x, int y, const unsigned char *string, int style);
@@ -480,9 +482,13 @@ extern void lcd_setfont(int font);
 extern int lcd_getfont(void);

 extern void lcd_puts_style_offset(int x, int y, const unsigned char *str,
-                                  int style, int offset);
+                                  int style, int x_offset);
+extern void lcd_puts_style_xyoffset(int x, int y, const unsigned char *str,
+                                  int style, int x_offset, int y_offset);
 extern void lcd_puts_scroll_style_offset(int x, int y, const unsigned char *string,
-                                  int style, int offset);
+                                  int style, int x_offset);
+extern void lcd_puts_scroll_style_xyoffset(int x, int y, const unsigned char *string,
+                                  int style, int x_offset, int y_offset);

 /* low level drawing function pointer arrays */
 #if LCD_DEPTH >= 8
diff --git a/firmware/export/scroll_engine.h b/firmware/export/scroll_engine.h
index ca94828..0fe6fe4 100644
--- a/firmware/export/scroll_engine.h
+++ b/firmware/export/scroll_engine.h
@@ -56,6 +56,7 @@ struct scrollinfo
     int y;      /* Position of the line on the screen (char co-ordinates) */
     int offset;
     int startx;
+    int y_offset; /* y offset of the line, used for pixel-accurate list scrolling */
 #ifdef HAVE_LCD_BITMAP
     int width;  /* length of line in pixels */
     int style; /* line style */