Commit 5d15be53 authored by Youness Alaoui's avatar Youness Alaoui
Browse files

initial commit

parents
all : test-menu-gtk test-menu-fb fbwhiptail
test-menu-gtk: test-menu-gtk.c cairo_menu.c cairo_utils.c
gcc -g -O0 -o $@ $^ \
`pkg-config --cflags --libs cairo` \
`pkg-config --cflags --libs gtk+-2.0` -lm
test-menu-fb: test-menu-fb.c cairo_menu.c cairo_utils.c cairo_linuxfb.c \
libcairo.a libpixman-1.a libpng16.a libz.a
gcc -g -O0 -o $@ $^ -lm
strip $@
fbwhiptail: fbwhiptail.c cairo_menu.c cairo_utils.c cairo_linuxfb.c \
libcairo.a libpixman-1.a libpng16.a libz.a
gcc -g -O0 -o $@ $^ -lm
strip $@
/*
* cairo_fb.c : Cairo Linux Framebuffer surface implementation
* Based on the demo application at https://github.com/toradex/cairo-fb-examples
*
* Copyright (c) 2015, Toradex AG
* Copyright (c) 2018, Youness Alaoui (KaKaRoTo)
*
* This project is licensed under the terms of the MIT license (see
* LICENSE)
*/
#include "cairo_linuxfb.h"
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/ioctl.h>
#include <linux/fb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/mman.h>
#include <time.h>
typedef struct _cairo_linuxfb_device {
int fb_fd;
unsigned char *fb_data;
unsigned char *previous_fb_data;
struct fb_var_screeninfo fb_vinfo;
struct fb_fix_screeninfo fb_finfo;
int num_buffers;
int buffer_id;
} cairo_linuxfb_device_t;
static cairo_user_data_key_t user_data_key;
/*
* Flip framebuffer, return the next buffer id which will be used or -1 if
* an operation failed
*/
int cairo_linuxfb_flip_buffer(cairo_surface_t *surface, int vsync, int bufid)
{
cairo_linuxfb_device_t *device;
device = cairo_surface_get_user_data (surface, &user_data_key);
if (device == NULL)
return -1;
if (device->buffer_id != bufid && bufid < device->num_buffers) {
/* Pan the framebuffer */
device->fb_vinfo.yoffset = device->fb_vinfo.yres * bufid;
if (ioctl(device->fb_fd, FBIOPAN_DISPLAY, &device->fb_vinfo)) {
perror("Error panning display");
return -1;
}
device->buffer_id = bufid;
}
if (vsync) {
int dummy = 0;
if (ioctl(device->fb_fd, FBIO_WAITFORVSYNC, &dummy)) {
perror("Error waiting for VSYNC");
return -1;
}
}
return (bufid+1) % device->num_buffers;
}
int cairo_linuxfb_get_resolution(cairo_surface_t *surface, int *xres, int *yres)
{
cairo_linuxfb_device_t *device;
device = cairo_surface_get_user_data (surface, &user_data_key);
if (device == NULL)
return -1;
*xres = device->fb_vinfo.xres;
*yres = device->fb_vinfo.yres;
return 0;
}
/* Destroy a cairo surface */
static void cairo_linuxfb_surface_destroy(void *device)
{
cairo_linuxfb_device_t *dev = (cairo_linuxfb_device_t *)device;
if (dev == NULL)
return;
memcpy (dev->fb_data, dev->previous_fb_data, dev->fb_finfo.smem_len);
free(dev->previous_fb_data);
munmap(dev->fb_data, dev->fb_finfo.smem_len);
close(dev->fb_fd);
free(dev);
}
/* Create a cairo surface using the specified framebuffer
* can return an error if fb driver doesn't support double buffering
*/
cairo_surface_t *cairo_linuxfb_surface_create(const char *fb_filename, int num_buffers)
{
cairo_surface_t *surface;
cairo_linuxfb_device_t *device;
device = malloc(sizeof(cairo_linuxfb_device_t));
if (device == NULL) {
perror ("Error: can't allocate structure");
return NULL;
}
// Open the file for reading and writing
device->fb_fd = open(fb_filename, O_RDWR);
if (device->fb_fd == -1) {
perror("Error: cannot open framebuffer device");
goto handle_open_error;
}
// Get variable screen information
if (ioctl(device->fb_fd, FBIOGET_VSCREENINFO, &device->fb_vinfo) == -1) {
perror("Error: reading variable information");
goto handle_ioctl_error;
}
/*
printf("Frame buffer var screen info : \n");
printf(" X Resolution : %u\n", device->fb_vinfo.xres);
printf(" Y Resolution : %u\n", device->fb_vinfo.yres);
printf(" X Virtual Resolution : %u\n", device->fb_vinfo.xres_virtual);
printf(" Y Virtual Resolution : %u\n", device->fb_vinfo.yres_virtual);
printf(" X Offset : %u\n", device->fb_vinfo.xoffset);
printf(" X Offset : %u\n", device->fb_vinfo.yoffset);
printf(" Bits per pixel : %u\n", device->fb_vinfo.bits_per_pixel);
printf(" Grayscale : %u\n", device->fb_vinfo.grayscale);
*/
/* Set virtual display size double the width for double buffering */
device->fb_vinfo.bits_per_pixel = 32;
device->fb_vinfo.yoffset = 0;
device->fb_vinfo.yres_virtual = device->fb_vinfo.yres * num_buffers;
if (ioctl(device->fb_fd, FBIOPUT_VSCREENINFO, &device->fb_vinfo)) {
perror("Error setting variable screen info from fb");
goto handle_ioctl_error;
}
// Get fixed screen information
if (ioctl(device->fb_fd, FBIOGET_FSCREENINFO, &device->fb_finfo) == -1) {
perror("Error reading fixed information");
goto handle_ioctl_error;
}
/*
printf("Frame buffer fixed screen info : \n");
printf(" ID : %16s\n", device->fb_finfo.id);
printf(" Line length : %u\n", device->fb_finfo.line_length);
printf(" Smem length : %u\n", device->fb_finfo.smem_len);
*/
// Map the device to memory
device->fb_data = (unsigned char *)mmap(0, device->fb_finfo.smem_len,
PROT_READ | PROT_WRITE, MAP_SHARED,
device->fb_fd, 0);
if (device->fb_data == MAP_FAILED) {
perror("Error: failed to map framebuffer device to memory");
goto handle_ioctl_error;
}
device->previous_fb_data = malloc (device->fb_finfo.smem_len);
if (device->previous_fb_data)
memcpy (device->previous_fb_data, device->fb_data,
device->fb_finfo.smem_len);
/* Create the cairo surface which will be used to draw to */
// TODO: Actually verify the format
surface = cairo_image_surface_create_for_data(device->fb_data,
CAIRO_FORMAT_RGB24,
device->fb_vinfo.xres,
device->fb_vinfo.yres_virtual,
cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32,
device->fb_vinfo.xres));
cairo_surface_set_user_data(surface, &user_data_key, device,
&cairo_linuxfb_surface_destroy);
return surface;
handle_ioctl_error:
close(device->fb_fd);
handle_open_error:
free(device);
return NULL;
}
/*
* cairo_fb.c : Cairo Linux Framebuffer surface implementation
* Based on the demo application at https://github.com/toradex/cairo-fb-examples
*
* Copyright (c) 2015, Toradex AG
* Copyright (c) 2018, Youness Alaoui (KaKaRoTo)
*
* This project is licensed under the terms of the MIT license (see
* LICENSE)
*/
#ifndef __CAIRO_LINUXFB_H__
#define __CAIRO_LINUXFB_H__
#include <cairo/cairo.h>
/*
* Flip framebuffer, return the next buffer id which will be used or -1 if
* an operation failed
*/
int cairo_linuxfb_flip_buffer(cairo_surface_t *surface, int vsync, int bufid);
int cairo_linuxfb_get_resolution(cairo_surface_t *surface, int *xres, int *yres);
/* Create a cairo surface using the specified framebuffer
* can return an error if fb driver doesn't support double buffering
*/
cairo_surface_t *cairo_linuxfb_surface_create(const char *fb_filename, int num_buffers);
#endif /* __CAIRO_LINUXFB_H__ */
This diff is collapsed.
/*
* cairo_menu.c : Cairo Menu drawing API
*
* Copyright (C) Youness Alaoui (KaKaRoTo)
* Written for PS3 in 2011. Refactored in 2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __CAIRO_MENU_H__
#define __CAIRO_MENU_H__
#include <cairo/cairo.h>
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
enum {
CAIRO_MENU_ALIGN_TOP = 0x01,
CAIRO_MENU_ALIGN_MIDDLE = 0x02,
CAIRO_MENU_ALIGN_BOTTOM = 0x04,
CAIRO_MENU_ALIGN_LEFT = 0x10,
CAIRO_MENU_ALIGN_CENTER = 0x20,
CAIRO_MENU_ALIGN_RIGHT = 0x40,
};
/**
* CairoMenuTextAlignment:
* @CAIRO_MENU_ALIGN_TOP_LEFT: Align the text to the top left corner of the menu
* item
* @CAIRO_MENU_ALIGN_TOP_CENTER: Align the text to the top and the center of the
* menu item
* @CAIRO_MENU_ALIGN_TOP_RIGHT: Align the text to the top right corner of the
* menu item
* @CAIRO_MENU_ALIGN_MIDDLE_LEFT: Align the text to the center vertically, and
* aligned to the left horizontally in the menu item
* @CAIRO_MENU_ALIGN_MIDDLE_CENTER:Align the text to the center vertically, and
* aligned to the center horizontally in the menu item
* @CAIRO_MENU_ALIGN_MIDDLE_RIGHT:Align the text to the center vertically, and
* aligned to the right horizontally menu item
* @CAIRO_MENU_ALIGN_BOTTOM_LEFT: Align the text to the bottom left corner of
* the menu item
* @CAIRO_MENU_ALIGN_BOTTOM_CENTER: Align the text to the bottom and the center
* of the menu item
* @CAIRO_MENU_ALIGN_BOTTOM_RIGHT: Align the text to the bottom right corner of
* the menu item
*
* Define how the text should be aligned and where to position it in a menu
* item's button
*/
typedef enum {
CAIRO_MENU_ALIGN_TOP_LEFT = CAIRO_MENU_ALIGN_TOP | CAIRO_MENU_ALIGN_LEFT,
CAIRO_MENU_ALIGN_TOP_CENTER = CAIRO_MENU_ALIGN_TOP | CAIRO_MENU_ALIGN_CENTER,
CAIRO_MENU_ALIGN_TOP_RIGHT = CAIRO_MENU_ALIGN_TOP | CAIRO_MENU_ALIGN_RIGHT,
CAIRO_MENU_ALIGN_MIDDLE_LEFT = CAIRO_MENU_ALIGN_MIDDLE | CAIRO_MENU_ALIGN_LEFT,
CAIRO_MENU_ALIGN_MIDDLE_CENTER = CAIRO_MENU_ALIGN_MIDDLE | CAIRO_MENU_ALIGN_CENTER,
CAIRO_MENU_ALIGN_MIDDLE_RIGHT = CAIRO_MENU_ALIGN_MIDDLE | CAIRO_MENU_ALIGN_RIGHT,
CAIRO_MENU_ALIGN_BOTTOM_LEFT = CAIRO_MENU_ALIGN_BOTTOM | CAIRO_MENU_ALIGN_LEFT,
CAIRO_MENU_ALIGN_BOTTOM_CENTER = CAIRO_MENU_ALIGN_BOTTOM | CAIRO_MENU_ALIGN_CENTER,
CAIRO_MENU_ALIGN_BOTTOM_RIGHT = CAIRO_MENU_ALIGN_BOTTOM | CAIRO_MENU_ALIGN_RIGHT,
} CairoMenuTextAlignment;
/**
* CairoMenuInput:
* @CAIRO_MENU_INPUT_UP: Move the selection one row up
* @CAIRO_MENU_INPUT_DOWN: Move the selection one row down
* @CAIRO_MENU_INPUT_LEFT: Move the selection one column to the left
* @CAIRO_MENU_INPUT_RIGHT: Move the selection one column to the right
*
* Defines the four possible input that the menu can handle for changing the
* selection
*/
typedef enum {
CAIRO_MENU_INPUT_UP,
CAIRO_MENU_INPUT_DOWN,
CAIRO_MENU_INPUT_LEFT,
CAIRO_MENU_INPUT_RIGHT,
} CairoMenuInput;
/**
* CairoMenuImagePosition:
* @CAIRO_MENU_IMAGE_POSITION_LEFT: Position the image on the left of the button
* @CAIRO_MENU_IMAGE_POSITION_RIGHT: Position the image on the right of the
* button
* @CAIRO_MENU_IMAGE_POSITION_TOP: Position the image on the top of the button
* @CAIRO_MENU_IMAGE_POSITION_BOTTOM: Position the image on the bottom of the
* button
*
* Defines the position of an image on a menu item. The image will be scaled
* so that it's height fits the menu item's height (for left/right positions)
* or so that it's width fits the menu item's width (for top/bottom positions)
*/
typedef enum {
CAIRO_MENU_IMAGE_POSITION_LEFT,
CAIRO_MENU_IMAGE_POSITION_RIGHT,
CAIRO_MENU_IMAGE_POSITION_TOP,
CAIRO_MENU_IMAGE_POSITION_BOTTOM,
} CairoMenuImagePosition;
/**
* CairoMenuColor:
* @red: The red value
* @green: The green value
* @blue: The blue value
* @alpha: The opacity
*
* A structure representing a single color, with values between 0.0 and 1.0
*/
typedef struct {
float red;
float green;
float blue;
float alpha;
} CairoMenuColor;
/**
* CairoMenuRectangle:
* @x: the x position of the rectangle
* @x: the y position of the rectangle
* @x: the width of the rectangle
* @x: the height of the rectangle
*
* A structure representing a rectangle
*/
typedef struct {
int x;
int y;
int width;
int height;
} CairoMenuRectangle;
/**
* CAIRO_MENU_DEFAULT_IPAD_X:
*
* Default horizontal internal padding of a menu's item
*/
#define CAIRO_MENU_DEFAULT_IPAD_X 10
/**
* CAIRO_MENU_DEFAULT_IPAD_Y:
*
* Default vertical internal padding of a menu's item
*/
#define CAIRO_MENU_DEFAULT_IPAD_Y 5
/**
* CAIRO_MENU_DEFAULT_PAD_X:
*
* Default horizontal padding of a menu's item
*/
#define CAIRO_MENU_DEFAULT_PAD_X 3
/**
* CAIRO_MENU_DEFAULT_PAD_Y:
*
* Default vertical padding of a menu's item
*/
#define CAIRO_MENU_DEFAULT_PAD_Y 2
/**
* CAIRO_MENU_DEFAULT_TEXT_COLOR:
*
* The default color to use for drawing text
*/
#define CAIRO_MENU_DEFAULT_TEXT_COLOR (CairoMenuColor) {1.0, 1.0, 1.0, 1.0}
typedef struct _CairoMenuItem CairoMenuItem;
typedef struct _CairoMenu CairoMenu;
/**
* CairoMenuDrawItemCb:
* @menu: The #CairoMenu being drawn
* @item: The #CairoMenuItem being drawn
* @selected: #TRUE if this item is currently selected, #FALSE otherwise
* @cr: The Cairo context to draw into
* @x: The X position where to draw in the Cairo surface.
* @y: The Y position where to draw in the Cairo surface.
* @user_data: Any user provided data for the callback
*
* This callback is called whenever the #CairoMenu needs to draw a menu item.
* The button's clipping area will be already set, and you need to draw on
* the (@x, @y) coordinate, up to(@x + @item->width - @item->ipad_x,
* @y + @item->height - @item->ipad_y).
*
*/
typedef int (*CairoMenuDrawItemCb) (CairoMenu *menu, CairoMenuItem *item,
int selected, cairo_t *cr, int x, int y, void *user_data);
/**
* CairoMenuItem:
* @image: An image to draw in the menu
* @image_position: Where to place the image on the menu
* @text: The text to show
* @text_size: The font size of the text to show
* @text_color: The color to use for the menu item's text
* @alignment: The alignment of the text
* @draw_cb: The drawing callback when the item needs to be drawn
* @draw_data: User data for the drawing callback
* @width: Item width
* @height: Item height
* @ipad_x: Internal horizontal padding
* @ipad_y: Internal vertical padding
* @enabled: Wether or not the item is enabled or not (greyed)
* @bg_image: Background image for non-selected item
* @bg_sel_image: Background image for selected item
* @index: The index of this item in the #CairoMenu. DO NOT modify this value.
*
* A structure representing a menu item, each attribute can be configured by
* modifying the structure.
*/
struct _CairoMenuItem {
cairo_surface_t *image;
CairoMenuImagePosition image_position;
char *text;
int text_size;
CairoMenuColor text_color;
CairoMenuTextAlignment alignment;
CairoMenuDrawItemCb draw_cb;
void *draw_data;
int width;
int height;
int ipad_x;
int ipad_y;
int enabled;
cairo_surface_t *bg_image;
cairo_surface_t *bg_sel_image;
/* Private - you can read, but don't modify */
int index;
};
/**
* CairoMenu:
* @surface: The surface where the menu gets drawn
* @rows: Total rows to show in the menu
* @columns: Total columns in the menu
* @default_item_width: The default width of each menu item
* @default_item_height: The default height of each menu item
* @pad_x: The horizontal padding between items
* @pad_y: The vertical padding between items
* @dropshadow_radius: The radius for a dropshadow to apply to all items. 0 for none.
* @bg_image: The default background image for non selected items
* @bg_sel_image: The default background image for selected items
* @disabled_image: An image to overlay on top of disabled items
* @nitems: Number of items in the menu
* @items: The items in the menu
* @selection: Currently selected item index
* @start_item: The first item to be drawn (!= 0 if scrolled)
* @dropshadow: A surface with the dropshadow to apply to all items.
*/
struct _CairoMenu {
cairo_surface_t *surface;
int rows;
int columns;
int default_item_width;
int default_item_height;
int pad_x;
int pad_y;
int dropshadow_radius;
cairo_surface_t *bg_image;
cairo_surface_t *bg_sel_image;
cairo_surface_t *disabled_image;
/* Private - can read but don't modify */
int nitems;
CairoMenuItem *items;
int selection;
int start_item;
cairo_surface_t *dropshadow;
};
/**
* cairo_menu_new:
* @surface: The surface associated with the menu, where it will get drawn. This
* surface will be referenced so if you do not want to hold a reference to it,
* then call cairo_surface_destroy() on it
* @rows: The number of rows to show in the menu, or -1 for infinite
* @columns: The number of columns to show in the menu, or -1 for infinite
* @default_item_width: The default width of each menu item
* @default_item_height: The default height of each menu item
*
* Create a new #CairoMenu with default settings and configured to show a
* specific number of rows or columns.
* If a row or column is set to -1 (infinite), then the menu will be scrollable
* in that direction.
* Both @rows and @columns cannot be set to -1 at the same time or an error is
* returned.
*
<note>
<para>
An item's width and height can always be modified after adding the item to
the menu, however, if the width and height of each item is different and the
menu has more than one row or column, then the behavior is undetermined.
It is best to use the same width/height for all the items in the menu.
</para>
</note>
*
* Returns: the newly created #CairoMenu or #NULL in case of an error.
*/
CairoMenu *cairo_menu_new (cairo_surface_t *surface, int rows, int columns,
int default_item_width, int default_item_height);
/**
* cairo_menu_new_full:
* @surface: The surface associated with the menu, where it will get drawn. This
* surface will be referenced so if you do not want to hold a reference to it,
* then call cairo_surface_destroy() on it
* @rows: The number of rows to show in the menu, or -1 for infinite
* @columns: The number of columns to show in the menu, or -1 for infinite
* @default_item_width: The default width of each menu item
* @default_item_height: The default height of each menu item
* @pad_x: The horizontal padding between items
* @pad_y: The vertical padding between items
* @dropshadow_radius: The radius for a dropshadow to add to the menus.
* @bg_image: The default background image for non selected items
* @bg_sel_image: The default background image for selected items
* @disabled_overlay: An image to overlay on top of disabled items
*
* Create a new #CairoMenu with all settings specified. This is a more complete
* version of cairo_menu_new() that allows you to specify each parameter for
* an increased sense of customization.
* Specifying 0 for the dropshadow radius means that it will be disabled. Note that
* the dropshadow will be unique and applied to all items, so the form and the size
* of the @bg_image and @bg_sel_image as well as each item's sizes must be the same
* otherwise the result is to be unexpected behavior.
* If @bg_image, @bg_sel_image, or @disabled_overlay are #NULL, then the default
* will be used.
*
* Returns: the newly created #CairoMenu or #NULL in case of an error.
*/
CairoMenu *cairo_menu_new_full (cairo_surface_t *surface, int rows, int columns,
int default_item_width, int default_item_height, int pad_x, int pad_y,
int dropshadow_radius, cairo_surface_t *bg_image,
cairo_surface_t *bg_sel_image, cairo_surface_t *disabled_overlay);
/**
* cairo_menu_add_item:
* @menu: The #CairoMenu to add the item to
* @text: The middle-left aligned text to show in the item
* @text_size: the size of the font for showing the text, or -1 for automatic
*
* Append a new item to the @menu.
* If the number of rows is infinite or if both the number of rows and columns
* are finite, the items are populated from left to right on each row,
* then subsequent rows are filled.
* If the number of columns is infinite, the items are populated from top to
* bottom on each column, then subsequent columns are filled.
*
* Returns: A unique identifier (the item's index) representing this item or
* -1 in case of an error (no room left).
* The created item can then be accessed using menu->items[index].
*/
int cairo_menu_add_item (CairoMenu *menu, const char *text, int text_size);
/**
* cairo_menu_add_item_full:
* @menu: The #CairoMenu to add the item to
* @image: An image to draw in the menu, or #NULL
* @image_position: Where to place the image on the menu
* @text: The text to show in the item
* @text_size: The font size of the text to show, or -1 for automatic
* @text_color: The color to use for the menu item's text
* @alignment: The alignment of the text
* @width: Item width
* @height: Item height
* @ipad_x: Internal horizontal padding
* @ipad_y: Internal vertical padding
* @enabled: Wether or not the item is enabled or not (greyed)
* @bg_image: Background image for non-selected item, or #NULL
* @bg_sel_image: Background image for selected item, or #NULL
* @draw_cb: The drawing callback when the item needs to be drawn, or #NULL
* @draw_data: User data for the drawing callback
*
* Append a new item to the @menu.
* This is a more complete version of cairo_menu_add_item() where you can
* specify all the options that you would normally modify directly in the
* #CairoMenuItem. The same rules apply as with cairo_menu_add_item().
* The @draw_cb is the callback used for drawing the item, it is usually set
* to an internal function that does the drawing, but it can be overridden if