1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * user intereface of image viewers (jpeg, png, etc.)
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
23 #include <lib/playback_control.h>
24 #include <lib/helper.h>
25 #include <lib/configfile.h>
26 #include "imageviewer.h"
39 /******************************* Globals ***********************************/
41 bool slideshow_enabled
= false; /* run slideshow */
42 bool running_slideshow
= false; /* loading image because of slideshw */
44 bool immediate_ata_off
= false; /* power down disk after loading */
46 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
47 /* are we using the plugin buffer or the audio buffer? */
51 /* Persistent configuration */
52 #define IMGVIEW_CONFIGFILE "imageviewer.cfg"
53 #define IMGVIEW_SETTINGS_MINVERSION 1
54 #define IMGVIEW_SETTINGS_VERSION 2
57 #define SS_MIN_TIMEOUT 1
58 #define SS_MAX_TIMEOUT 20
59 #define SS_DEFAULT_TIMEOUT 5
62 /* needed for value of settings */
63 #include "jpeg/yuv2rgb.h"
67 struct imgview_settings settings
=
75 static struct imgview_settings old_settings
;
77 static struct configdata config
[] =
80 { TYPE_ENUM
, 0, COLOUR_NUM_MODES
, { .int_p
= &settings
.jpeg_colour_mode
},
81 "Colour Mode", (char *[]){ "Colour", "Grayscale" } },
82 { TYPE_ENUM
, 0, DITHER_NUM_MODES
, { .int_p
= &settings
.jpeg_dither_mode
},
83 "Dither Mode", (char *[]){ "None", "Ordered", "Diffusion" } },
85 { TYPE_INT
, SS_MIN_TIMEOUT
, SS_MAX_TIMEOUT
,
86 { .int_p
= &settings
.ss_timeout
}, "Slideshow Time", NULL
},
90 static fb_data
* old_backdrop
;
93 /**************** begin Application ********************/
96 /************************* Globals ***************************/
98 #if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER)
99 static fb_data rgb_linebuf
[LCD_WIDTH
]; /* Line buffer for scrolling when
100 DITHER_DIFFUSION is set */
103 /* my memory pool (from the mp3 buffer) */
104 static char print
[32]; /* use a common snprintf() buffer */
105 /* the remaining free part of the buffer for loaded+resized images */
106 static unsigned char* buf
;
107 static ssize_t buf_size
;
109 static int ds
, ds_min
, ds_max
; /* downscaling and limits */
110 static struct image_info image_info
;
112 static struct tree_context
*tree
;
114 /* the current full file name */
115 static char np_file
[MAX_PATH
];
116 static int curfile
= 0, direction
= DIR_NONE
, entries
= 0;
118 /* list of the supported image files */
119 static char **file_pt
;
121 /************************* Implementation ***************************/
123 /*Read directory contents for scrolling. */
124 static void get_pic_list(void)
127 struct entry
*dircache
;
129 tree
= rb
->tree_get_context();
130 dircache
= tree
->dircache
;
132 file_pt
= (char **) buf
;
134 /* Remove path and leave only the name.*/
135 pname
= rb
->strrchr(np_file
,'/');
138 for (i
= 0; i
< tree
->filesindir
; i
++)
140 if (!(dircache
[i
].attr
& ATTR_DIRECTORY
)
141 && img_ext(rb
->strrchr(dircache
[i
].name
,'.')))
143 file_pt
[entries
] = dircache
[i
].name
;
144 /* Set Selected File. */
145 if (!rb
->strcmp(file_pt
[entries
], pname
))
151 buf
+= (entries
* sizeof(char**));
152 buf_size
-= (entries
* sizeof(char**));
155 static int change_filename(int direct
)
157 bool file_erased
= (file_pt
[curfile
] == NULL
);
160 curfile
+= (direct
== DIR_PREV
? entries
- 1: 1);
161 if (curfile
>= entries
)
166 /* remove 'erased' file names from list. */
168 for (count
= i
= 0; i
< entries
; i
++)
172 if (file_pt
[i
] != NULL
)
173 file_pt
[count
++] = file_pt
[i
];
180 rb
->splash(HZ
, "No supported files");
184 if (rb
->strlen(tree
->currdir
) > 1)
186 rb
->strcpy(np_file
, tree
->currdir
);
187 rb
->strcat(np_file
, "/");
190 rb
->strcpy(np_file
, tree
->currdir
);
192 rb
->strcat(np_file
, file_pt
[curfile
]);
197 /* switch off overlay, for handling SYS_ events */
198 static void cleanup(void *parameter
)
206 #define VSCROLL (LCD_HEIGHT/8)
207 #define HSCROLL (LCD_WIDTH/10)
209 #define ZOOM_IN 100 /* return codes for below function */
212 #if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER)
213 static bool set_option_grayscale(void)
215 bool gray
= settings
.jpeg_colour_mode
== COLOURMODE_GRAY
;
216 rb
->set_bool("Grayscale", &gray
);
217 settings
.jpeg_colour_mode
= gray
? COLOURMODE_GRAY
: COLOURMODE_COLOUR
;
221 static bool set_option_dithering(void)
223 static const struct opt_items dithering
[DITHER_NUM_MODES
] = {
224 [DITHER_NONE
] = { "Off", -1 },
225 [DITHER_ORDERED
] = { "Ordered", -1 },
226 [DITHER_DIFFUSION
] = { "Diffusion", -1 },
229 rb
->set_option("Dithering", &settings
.jpeg_dither_mode
, INT
,
230 dithering
, DITHER_NUM_MODES
, NULL
);
234 MENUITEM_FUNCTION(grayscale_item
, 0, "Greyscale",
235 set_option_grayscale
, NULL
, NULL
, Icon_NOICON
);
236 MENUITEM_FUNCTION(dithering_item
, 0, "Dithering",
237 set_option_dithering
, NULL
, NULL
, Icon_NOICON
);
238 MAKE_MENU(display_menu
, "Display Options", NULL
, Icon_NOICON
,
239 &grayscale_item
, &dithering_item
);
241 static void display_options(void)
243 rb
->do_menu(&display_menu
, NULL
, NULL
, false);
245 #endif /* defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER) */
247 static int show_menu(void) /* return 1 to quit */
250 rb
->lcd_set_backdrop(old_backdrop
);
251 #ifdef HAVE_LCD_COLOR
252 rb
->lcd_set_foreground(rb
->global_settings
->fg_color
);
253 rb
->lcd_set_background(rb
->global_settings
->bg_color
);
255 rb
->lcd_set_foreground(LCD_BLACK
);
256 rb
->lcd_set_background(LCD_WHITE
);
266 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
267 MIID_SHOW_PLAYBACK_MENU
,
269 #if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER)
270 MIID_DISPLAY_OPTIONS
,
275 MENUITEM_STRINGLIST(menu
, MENU_TITLE
, NULL
,
276 "Return", "Toggle Slideshow Mode",
277 "Change Slideshow Time",
278 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
279 "Show Playback Menu",
281 #if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER)
286 static const struct opt_items slideshow
[2] = {
291 result
=rb
->do_menu(&menu
, NULL
, NULL
, false);
297 case MIID_TOGGLE_SS_MODE
:
298 rb
->set_option("Toggle Slideshow", &slideshow_enabled
, INT
,
299 slideshow
, 2, NULL
);
301 case MIID_CHANGE_SS_MODE
:
302 rb
->set_int("Slideshow Time", "s", UNIT_SEC
,
303 &settings
.ss_timeout
, NULL
, 1,
304 SS_MIN_TIMEOUT
, SS_MAX_TIMEOUT
, NULL
);
307 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
308 case MIID_SHOW_PLAYBACK_MENU
:
311 playback_control(NULL
);
315 rb
->splash(HZ
, "Cannot restart playback");
319 #if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER)
320 case MIID_DISPLAY_OPTIONS
:
330 /* change ata spindown time based on slideshow time setting */
331 immediate_ata_off
= false;
332 rb
->storage_spindown(rb
->global_settings
->disk_spindown
);
334 if (slideshow_enabled
)
336 if(settings
.ss_timeout
< 10)
338 /* slideshow times < 10s keep disk spinning */
339 rb
->storage_spindown(0);
341 else if (!rb
->mp3_is_playing())
343 /* slideshow times > 10s and not playing: ata_off after load */
344 immediate_ata_off
= true;
349 rb
->lcd_set_backdrop(NULL
);
350 rb
->lcd_set_foreground(LCD_WHITE
);
351 rb
->lcd_set_background(LCD_BLACK
);
353 rb
->lcd_clear_display();
357 /* Pan the viewing window right - move image to the left and fill in
358 the right-hand side */
359 static void pan_view_right(struct image_info
*info
)
363 move
= MIN(HSCROLL
, info
->width
- info
->x
- LCD_WIDTH
);
366 MYXLCD(scroll_left
)(move
); /* scroll left */
368 draw_image_rect(info
, LCD_WIDTH
- move
, 0, move
, info
->height
-info
->y
);
373 /* Pan the viewing window left - move image to the right and fill in
374 the left-hand side */
375 static void pan_view_left(struct image_info
*info
)
379 move
= MIN(HSCROLL
, info
->x
);
382 MYXLCD(scroll_right
)(move
); /* scroll right */
384 draw_image_rect(info
, 0, 0, move
, info
->height
-info
->y
);
389 /* Pan the viewing window up - move image down and fill in
391 static void pan_view_up(struct image_info
*info
)
395 move
= MIN(VSCROLL
, info
->y
);
398 MYXLCD(scroll_down
)(move
); /* scroll down */
400 #if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER)
401 if (settings
.jpeg_dither_mode
== DITHER_DIFFUSION
)
403 /* Draw over the band at the top of the last update
404 caused by lack of error history on line zero. */
405 move
= MIN(move
+ 1, info
->y
+ info
->height
);
408 draw_image_rect(info
, 0, 0, info
->width
-info
->x
, move
);
413 /* Pan the viewing window down - move image up and fill in
415 static void pan_view_down(struct image_info
*info
)
419 move
= MIN(VSCROLL
, info
->height
- info
->y
- LCD_HEIGHT
);
422 MYXLCD(scroll_up
)(move
); /* scroll up */
424 #if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER)
425 if (settings
.jpeg_dither_mode
== DITHER_DIFFUSION
)
427 /* Save the line that was on the last line of the display
428 and draw one extra line above then recover the line with
429 image data that had an error history when it was drawn.
432 rb
->memcpy(rgb_linebuf
,
433 rb
->lcd_framebuffer
+ (LCD_HEIGHT
- move
)*LCD_WIDTH
,
434 LCD_WIDTH
*sizeof (fb_data
));
438 draw_image_rect(info
, 0, LCD_HEIGHT
- move
, info
->width
-info
->x
, move
);
440 #if defined(HAVE_LCD_COLOR) && defined(JPEG_VIEWER)
441 if (settings
.jpeg_dither_mode
== DITHER_DIFFUSION
)
443 /* Cover the first row drawn with previous image data. */
444 rb
->memcpy(rb
->lcd_framebuffer
+ (LCD_HEIGHT
- move
)*LCD_WIDTH
,
445 rgb_linebuf
, LCD_WIDTH
*sizeof (fb_data
));
453 /* interactively scroll around the image */
454 static int scroll_bmp(struct image_info
*info
)
461 if (slideshow_enabled
)
462 button
= rb
->button_get_w_tmo(settings
.ss_timeout
* HZ
);
464 button
= rb
->button_get(true);
466 running_slideshow
= false;
471 if (entries
> 1 && info
->width
<= LCD_WIDTH
472 && info
->height
<= LCD_HEIGHT
)
473 return change_filename(DIR_PREV
);
474 case IMGVIEW_LEFT
| BUTTON_REPEAT
:
479 if (entries
> 1 && info
->width
<= LCD_WIDTH
480 && info
->height
<= LCD_HEIGHT
)
481 return change_filename(DIR_NEXT
);
482 case IMGVIEW_RIGHT
| BUTTON_REPEAT
:
483 pan_view_right(info
);
487 case IMGVIEW_UP
| BUTTON_REPEAT
:
492 case IMGVIEW_DOWN
| BUTTON_REPEAT
:
497 if (!slideshow_enabled
)
499 running_slideshow
= true;
501 return change_filename(DIR_NEXT
);
504 #ifdef IMGVIEW_SLIDE_SHOW
505 case IMGVIEW_SLIDE_SHOW
:
506 slideshow_enabled
= !slideshow_enabled
;
507 running_slideshow
= slideshow_enabled
;
511 #ifdef IMGVIEW_NEXT_REPEAT
512 case IMGVIEW_NEXT_REPEAT
:
516 return change_filename(DIR_NEXT
);
519 #ifdef IMGVIEW_PREVIOUS_REPEAT
520 case IMGVIEW_PREVIOUS_REPEAT
:
522 case IMGVIEW_PREVIOUS
:
524 return change_filename(DIR_PREV
);
527 case IMGVIEW_ZOOM_IN
:
528 #ifdef IMGVIEW_ZOOM_PRE
529 if (lastbutton
!= IMGVIEW_ZOOM_PRE
)
535 case IMGVIEW_ZOOM_OUT
:
536 #ifdef IMGVIEW_ZOOM_PRE
537 if (lastbutton
!= IMGVIEW_ZOOM_PRE
)
542 #ifdef IMGVIEW_RC_MENU
543 case IMGVIEW_RC_MENU
:
547 grey_show(false); /* switch off greyscale overlay */
549 if (show_menu() == 1)
553 grey_show(true); /* switch on greyscale overlay */
555 draw_image_rect(info
, 0, 0,
556 info
->width
-info
->x
, info
->height
-info
->y
);
561 if (rb
->default_event_handler_ex(button
, cleanup
, NULL
)
562 == SYS_USB_CONNECTED
)
563 return PLUGIN_USB_CONNECTED
;
568 if (button
!= BUTTON_NONE
)
573 /********************* main function *************************/
575 /* callback updating a progress meter while image decoding */
576 void cb_progress(int current
, int total
)
578 rb
->yield(); /* be nice to the other threads */
579 if(!running_slideshow
)
581 rb
->gui_scrollbar_draw(rb
->screens
[SCREEN_MAIN
],
582 0, LCD_HEIGHT
-8, LCD_WIDTH
, 8,
583 total
, 0, current
, HORIZONTAL
);
584 rb
->lcd_update_rect(0, LCD_HEIGHT
-8, LCD_WIDTH
, 8);
589 /* in slideshow mode, keep gui interference to a minimum */
590 rb
->gui_scrollbar_draw(rb
->screens
[SCREEN_MAIN
],
591 0, LCD_HEIGHT
-4, LCD_WIDTH
, 4,
592 total
, 0, current
, HORIZONTAL
);
593 rb
->lcd_update_rect(0, LCD_HEIGHT
-4, LCD_WIDTH
, 4);
598 /* how far can we zoom in without running out of memory */
599 static int min_downscale(int bufsize
)
603 if (img_mem(8) > bufsize
)
604 return 0; /* error, too large, even 1:8 doesn't fit */
606 while (downscale
> 1 && img_mem(downscale
/2) <= bufsize
)
612 /* how far can we zoom out, to fit image into the LCD */
613 static int max_downscale(struct image_info
*info
)
617 while (downscale
< 8 && (info
->x_size
/downscale
> LCD_WIDTH
618 || info
->y_size
/downscale
> LCD_HEIGHT
))
626 /* set the view to the given center point, limit if necessary */
627 static void set_view(struct image_info
*info
, int cx
, int cy
)
631 /* plain center to available width/height */
632 x
= cx
- MIN(LCD_WIDTH
, info
->width
) / 2;
633 y
= cy
- MIN(LCD_HEIGHT
, info
->height
) / 2;
635 /* limit against upper image size */
636 x
= MIN(info
->width
- LCD_WIDTH
, x
);
637 y
= MIN(info
->height
- LCD_HEIGHT
, y
);
639 /* limit against negative side */
643 info
->x
= x
; /* set the values */
647 /* calculate the view center based on the bitmap position */
648 static void get_view(struct image_info
*info
, int *p_cx
, int *p_cy
)
650 *p_cx
= info
->x
+ MIN(LCD_WIDTH
, info
->width
) / 2;
651 *p_cy
= info
->y
+ MIN(LCD_HEIGHT
, info
->height
) / 2;
654 /* load, decode, display the image */
655 static int load_and_show(char* filename
, struct image_info
*info
)
662 rb
->lcd_set_foreground(LCD_WHITE
);
663 rb
->lcd_set_background(LCD_BLACK
);
664 rb
->lcd_set_backdrop(NULL
);
666 rb
->lcd_clear_display();
668 rb
->memset(info
, 0, sizeof(*info
));
669 remaining
= buf_size
;
671 if (rb
->button_get(false) == IMGVIEW_MENU
)
672 status
= PLUGIN_ABORT
;
674 status
= load_image(filename
, info
, buf
, &remaining
);
676 if (status
== PLUGIN_OUTOFMEM
)
678 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
681 rb
->lcd_setfont(FONT_SYSFIXED
);
682 rb
->lcd_clear_display();
683 rb
->snprintf(print
,sizeof(print
),"%s:",rb
->strrchr(filename
,'/')+1);
684 rb
->lcd_puts(0,0,print
);
685 rb
->lcd_puts(0,1,"Not enough plugin memory!");
686 rb
->lcd_puts(0,2,"Zoom In: Stop playback.");
688 rb
->lcd_puts(0,3,"Left/Right: Skip File.");
689 rb
->lcd_puts(0,4,"Show Menu: Quit.");
691 rb
->lcd_setfont(FONT_UI
);
693 rb
->button_clear_queue();
697 int button
= rb
->button_get(true);
700 case IMGVIEW_ZOOM_IN
:
702 buf
= rb
->plugin_get_audio_buffer((size_t *)&buf_size
);
703 /*try again this file, now using the audio buffer */
705 #ifdef IMGVIEW_RC_MENU
706 case IMGVIEW_RC_MENU
:
714 rb
->lcd_clear_display();
715 return change_filename(DIR_PREV
);
722 rb
->lcd_clear_display();
723 return change_filename(DIR_NEXT
);
727 if(rb
->default_event_handler_ex(button
, cleanup
, NULL
)
728 == SYS_USB_CONNECTED
)
729 return PLUGIN_USB_CONNECTED
;
737 rb
->splash(HZ
, "Out of Memory");
738 file_pt
[curfile
] = NULL
;
739 return change_filename(direction
);
742 else if (status
== PLUGIN_ERROR
)
744 file_pt
[curfile
] = NULL
;
745 return change_filename(direction
);
747 else if (status
== PLUGIN_ABORT
) {
748 rb
->splash(HZ
, "aborted");
752 ds_max
= max_downscale(info
); /* check display constraint */
753 ds_min
= min_downscale(remaining
); /* check memory constraint */
756 #if UNSCALED_IS_AVAILABLE
757 /* Can not resize the image but original one is available, so use it. */
760 /* not enough memory to decode image. */
761 rb
->splash(HZ
, "too large");
762 file_pt
[curfile
] = NULL
;
763 return change_filename(direction
);
766 else if (ds_max
< ds_min
)
769 ds
= ds_max
; /* initialize setting */
770 cx
= info
->x_size
/ds
/2; /* center the view */
771 cy
= info
->y_size
/ds
/2;
773 do /* loop the image prepare and decoding when zoomed */
775 status
= get_image(info
, ds
); /* decode or fetch from cache */
776 if (status
== PLUGIN_ERROR
)
778 file_pt
[curfile
] = NULL
;
779 return change_filename(direction
);
782 set_view(info
, cx
, cy
);
784 if(!running_slideshow
)
786 rb
->snprintf(print
, sizeof(print
), "showing %dx%d",
787 info
->width
, info
->height
);
788 rb
->lcd_puts(0, 3, print
);
792 MYLCD(clear_display
)();
793 draw_image_rect(info
, 0, 0,
794 info
->width
-info
->x
, info
->height
-info
->y
);
798 grey_show(true); /* switch on greyscale overlay */
801 /* drawing is now finished, play around with scrolling
802 * until you press OFF or connect USB
806 status
= scroll_bmp(info
);
807 if (status
== ZOOM_IN
)
809 #if UNSCALED_IS_AVAILABLE
815 #if UNSCALED_IS_AVAILABLE
816 /* if 1/1 is always available, jump ds from ds_min to 1. */
817 int zoom
= (ds
== ds_min
)? ds_min
: 2;
821 ds
/= zoom
; /* reduce downscaling to zoom in */
822 get_view(info
, &cx
, &cy
);
823 cx
*= zoom
; /* prepare the position in the new image */
830 if (status
== ZOOM_OUT
)
834 #if UNSCALED_IS_AVAILABLE
835 /* if ds is 1 and ds_min is > 1, jump ds to ds_min. */
836 int zoom
= (ds
< ds_min
)? ds_min
: 2;
840 ds
*= zoom
; /* increase downscaling to zoom out */
841 get_view(info
, &cx
, &cy
);
842 cx
/= zoom
; /* prepare the position in the new image */
852 grey_show(false); /* switch off overlay */
854 rb
->lcd_clear_display();
856 while (status
!= PLUGIN_OK
&& status
!= PLUGIN_USB_CONNECTED
857 && status
!= PLUGIN_OTHER
);
864 /******************** Plugin entry point *********************/
866 enum plugin_status
plugin_start(const void* parameter
)
870 long greysize
; /* helper */
873 old_backdrop
= rb
->lcd_get_backdrop();
876 if(!parameter
) return PLUGIN_ERROR
;
878 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
879 buf
= rb
->plugin_get_buffer((size_t *)&buf_size
);
881 buf
= rb
->plugin_get_audio_buffer((size_t *)&buf_size
);
884 rb
->strcpy(np_file
, parameter
);
887 if(!entries
) return PLUGIN_ERROR
;
889 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
890 if(!rb
->audio_status())
893 buf
= rb
->plugin_get_audio_buffer((size_t *)&buf_size
);
898 if (!grey_init(buf
, buf_size
, GREY_ON_COP
,
899 LCD_WIDTH
, LCD_HEIGHT
, &greysize
))
901 rb
->splash(HZ
, "grey buf error");
905 buf_size
-= greysize
;
908 /* should be ok to just load settings since the plugin itself has
909 just been loaded from disk and the drive should be spinning */
910 configfile_load(IMGVIEW_CONFIGFILE
, config
,
911 ARRAYLEN(config
), IMGVIEW_SETTINGS_MINVERSION
);
912 rb
->memcpy(&old_settings
, &settings
, sizeof (settings
));
914 /* Turn off backlight timeout */
915 backlight_force_on(); /* backlight control in lib/helper.c */
919 condition
= load_and_show(np_file
, &image_info
);
920 } while (condition
!= PLUGIN_OK
&& condition
!= PLUGIN_USB_CONNECTED
921 && condition
!= PLUGIN_ERROR
);
923 if (rb
->memcmp(&settings
, &old_settings
, sizeof (settings
)))
925 /* Just in case drive has to spin, keep it from looking locked */
926 rb
->splash(0, "Saving Settings");
927 configfile_save(IMGVIEW_CONFIGFILE
, config
,
928 ARRAYLEN(config
), IMGVIEW_SETTINGS_VERSION
);
932 /* set back ata spindown time in case we changed it */
933 rb
->storage_spindown(rb
->global_settings
->disk_spindown
);
936 /* Turn on backlight timeout (revert to settings) */
937 backlight_use_settings(); /* backlight control in lib/helper.c */
940 grey_release(); /* deinitialize */