From 65a388d0e7f8e1e55160fdb970e2919eb1115330 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Revol?= Date: Mon, 5 Sep 2022 01:31:00 +0200 Subject: [PATCH] WIP: SDL display support --- cmake/ConkyBuildOptions.cmake | 7 + cmake/ConkyPlatformChecks.cmake | 23 ++ cmake/config.h.in | 4 + src/CMakeLists.txt | 7 + src/display-output.cc | 2 + src/display-sdl.cc | 506 ++++++++++++++++++++++++++++++++ src/display-sdl.hh | 86 ++++++ 7 files changed, 635 insertions(+) create mode 100644 src/display-sdl.cc create mode 100644 src/display-sdl.hh diff --git a/cmake/ConkyBuildOptions.cmake b/cmake/ConkyBuildOptions.cmake index 713430bd66..23c5bf8825 100644 --- a/cmake/ConkyBuildOptions.cmake +++ b/cmake/ConkyBuildOptions.cmake @@ -185,6 +185,10 @@ else(BUILD_X11) set(BUILD_NVIDIA false) endif(BUILD_X11) +option(BUILD_SDL "Build SDL 1.2 support" false) +if(BUILD_SDL) +endif(BUILD_SDL) + # if we build with any GUI support if(BUILD_X11) set(BUILD_GUI true) @@ -192,6 +196,9 @@ endif(BUILD_X11) if(BUILD_WAYLAND) set(BUILD_GUI true) endif(BUILD_WAYLAND) +if(BUILD_SDL) + set(BUILD_GUI true) +endif(BUILD_SDL) if(OWN_WINDOW) option(BUILD_ARGB "Build ARGB (real transparency) support" true) diff --git a/cmake/ConkyPlatformChecks.cmake b/cmake/ConkyPlatformChecks.cmake index ac1cfd4857..82fbf89c82 100644 --- a/cmake/ConkyPlatformChecks.cmake +++ b/cmake/ConkyPlatformChecks.cmake @@ -480,6 +480,29 @@ if(BUILD_X11) endif(BUILD_LUA_RSVG) endif(BUILD_X11) +# check for SDL +if(BUILD_SDL) + include(FindSDL) + include(FindSDL_ttf) + find_package(SDL) + if(SDL_FOUND) + set(conky_includes ${conky_includes} ${SDL_INCLUDE_DIR}) + set(conky_libs ${conky_libs} ${SDL_LIBRARIES}) + find_package(SDL_ttf) + if(SDLTTF_FOUND) + set(conky_includes ${conky_includes} ${SDL_TTF_INCLUDE_DIR}) + set(conky_libs ${conky_libs} ${SDL_TTF_LIBRARIES}) + else(SDLTTL_FOUND) + message(FATAL_ERROR "Unable to find SDL_ttf library") + endif(SDLTTF_FOUND) + check_include_files(SDL_gfxPrimitives.h HAVE_SDL_GFXPRIMITIVES_H) + find_library(SDLGFX_LIBRARY SDL_gfx) + else(SDL_FOUND) + message(FATAL_ERROR "Unable to find SDL library") + endif(SDL_FOUND) + check_include_files(ftw.h HAVE_FTW_H) +endif(BUILD_SDL) + if(BUILD_AUDACIOUS) set(WANT_GLIB true) pkg_check_modules(NEW_AUDACIOUS audacious>=1.4.0) diff --git a/cmake/config.h.in b/cmake/config.h.in index 54936c3f41..316f158ad1 100644 --- a/cmake/config.h.in +++ b/cmake/config.h.in @@ -29,6 +29,8 @@ #cmakedefine HAVE_SYS_INOTIFY_H 1 #cmakedefine HAVE_DIRENT_H 1 +#cmakedefine HAVE_FTW_H 1 + #cmakedefine HAVE_SOME_SOUNDCARD_H 1 #cmakedefine HAVE_LINUX_SOUNDCARD_H 1 @@ -117,6 +119,8 @@ #cmakedefine BUILD_HTTP 1 +#cmakedefine BUILD_SDL 1 + #cmakedefine BUILD_GUI 1 #cmakedefine BUILD_ICONV 1 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 56cfd84d3b..a3aa6a2e32 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -123,6 +123,8 @@ set(conky_sources display-ncurses.hh display-http.cc display-http.hh + display-sdl.cc + display-sdl.hh display-x11.cc display-x11.hh display-wayland.cc @@ -251,6 +253,11 @@ if(BUILD_GUI) set(optional_sources ${optional_sources} ${gui}) endif(BUILD_GUI) +if(BUILD_SDL) + set(sdl_srcs ) + set(optional_sources ${optional_sources} ${sdl_srcs}) +endif(BUILD_SDL) + if(BUILD_WAYLAND) set(wl_srcs wl.cc wl.h xdg-shell-protocol.c wlr-layer-shell-protocol.c) set(optional_sources ${optional_sources} ${wl_srcs}) diff --git a/src/display-output.cc b/src/display-output.cc index b9f49e678a..d2f6637649 100644 --- a/src/display-output.cc +++ b/src/display-output.cc @@ -57,6 +57,7 @@ extern void init_file_output(); extern void init_http_output(); extern void init_x11_output(); extern void init_wayland_output(); +extern void init_sdl_output(); /* * The selected and active display output. @@ -113,6 +114,7 @@ bool initialize_display_outputs() { init_http_output(); init_x11_output(); init_wayland_output(); + init_sdl_output(); std::vector outputs; outputs.reserve(display_outputs->size()); diff --git a/src/display-sdl.cc b/src/display-sdl.cc new file mode 100644 index 0000000000..cbc6c377ea --- /dev/null +++ b/src/display-sdl.cc @@ -0,0 +1,506 @@ +/* + * + * Conky, a system monitor, based on torsmo + * + * Please see COPYING for details + * + * Copyright (C) 2018-2022 François Revol et al. + * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen + * Copyright (c) 2005-2021 Brenden Matthews, Philip Kovacs, et. al. + * (see AUTHORS) + * All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include + +#ifdef BUILD_SDL +#include +#include +#endif /* BUILD_SDL */ + +#ifdef HAVE_FTW_H +#include +#endif +#include + +#include +#include +#include + +#include "conky.h" +#include "display-sdl.hh" +#include "gui.h" +#include "llua.h" +//#include "x11.h" +#ifdef BUILD_SDL +#include "fonts.h" +#endif + +/* TODO: cleanup global namespace */ +#ifdef BUILD_SDL + +#ifndef DIRSEP_CHAR +#define DIRSEP_CHAR '/' +//TODO: win +#endif + +conky::simple_config_setting out_to_sdl("out_to_sdl", false, false); +#ifndef BUILD_X11 +/* Catch up as a generic "out_to_gui" for now */ +conky::simple_config_setting out_to_x("out_to_x", false, false); +#endif + +// TODO: cleanup externs (move to conky.h ?) +#ifdef OWN_WINDOW +extern int fixed_size, fixed_pos; +#endif +extern int text_start_x, text_start_y; /* text start position in window */ +extern int text_offset_x, text_offset_y; /* offset for start position */ +extern int text_width, + text_height; /* initially 1 so no zero-sized window is created */ +extern double current_update_time, next_update_time, last_update_time; +void update_text(); +extern int need_to_update; +int get_border_total(); +extern conky::range_config_setting maximum_width; +extern long current_color; + +SDL_Surface *surface = nullptr; +static unsigned int thefont; + +/* for sdl_fonts */ +struct sdl_font_list { + TTF_Font *font; + + sdl_font_list() + : font(nullptr) + { + } +}; + +static std::vector sdl_fonts; /* indexed by selected_font */ + +static void SDL_create_window(); + +struct _sdl_stuff_s { +#if 0 + Region region; +#ifdef BUILD_XDAMAGE + Damage damage; + XserverRegion region2, part; + int event_base, error_base; +#endif +#endif +} sdl_stuff; + +#endif /* BUILD_SDL */ + +namespace conky { +namespace { + +#ifdef BUILD_SDL +conky::display_output_sdl sdl_output; +#else +conky::disabled_display_output sdl_output_disabled("sdl", "BUILD_SDL"); +#endif + +} // namespace +extern void init_sdl_output() {} + +namespace priv {} // namespace priv + +#ifdef BUILD_SDL + +display_output_sdl::display_output_sdl() : display_output_base("sdl") { + is_graphical = true; + priority = 2; +} + +bool display_output_sdl::detect() { + if (out_to_sdl.get(*state)) { + DBGP2("Display output '%s' enabled in config.", name.c_str()); + return true; + } +#ifndef BUILD_X11 + if (out_to_x.get(*state)) { + DBGP2("Display output '%s' enabled in config as fallback of 'out_to_x'.", + name.c_str()); + return true; + } +#endif + return false; +} + +bool display_output_sdl::initialize() { + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) == -1) { + NORM_ERR("SDL_Init(): %s", SDL_GetError()); + return false; + } + TTF_Init(); + + int b = border_inner_margin.get(*state) + border_width.get(*state) + + border_outer_margin.get(*state); + int flags = SDL_SWSURFACE | SDL_RESIZABLE; + int bpp = 32; + surface = SDL_SetVideoMode(b, b, bpp, flags); + if (surface == NULL) { + NORM_ERR("SDL_SetVideoMode: %s", SDL_GetError()); + return false; + } + + SDL_WM_SetCaption("Conky", "Conky"); + + setup_fonts(); + load_fonts(utf8_mode.get(*state)); + update_text_area(); /* to position text/window on screen */ + + draw_stuff(); + + selected_font = 0; + update_text_area(); /* to get initial size of the window */ + + return true; +} + +bool display_output_sdl::shutdown() { + TTF_Quit(); + SDL_Quit(); + return false; +} + +static Uint32 SDL_timer_callback(Uint32, void *param) { + SDL_PushEvent(static_cast(param)); + return 0; +} + +bool display_output_sdl::main_loop_wait(double t) { + /* wait for SDL event or timeout */ + /* SDL_WaitEventTimeout is SDL2 so we must set a timer */ + SDL_TimerID timer; + static const SDL_UserEvent timeout = {SDL_USEREVENT, 1, NULL, NULL}; + SDL_Event ev; + int r; + + t = std::min(std::max(t, 0.0), active_update_interval()); + timer = SDL_AddTimer(static_cast(t * 1000.0), SDL_timer_callback, + (void *)&timeout); + + SDL_ClearError(); + + r = SDL_WaitEvent(NULL); + + SDL_RemoveTimer(timer); + + if (r == 0) { + /* error */ + NORM_ERR("Error in SDL_WaitEvent(): %s", SDL_GetError()); + } + + if (need_to_update != 0) { + need_to_update = 0; + selected_font = 0; + update_text_area(); + + int changed = 0; + int border_total = get_border_total(); + + /* resize window if it isn't right size */ + if (/*(fixed_size == 0) &&*/ + (text_width + 2 * border_total != surface->w || + text_height + 2 * border_total != surface->h)) { + int width = text_width + 2 * border_total; + int height = text_height + 2 * border_total; + int flags = SDL_SWSURFACE | SDL_RESIZABLE; + int bpp = 32; + printf("resize %dx%d\n", width, height); + surface = SDL_SetVideoMode(width, height, bpp, flags); + if (surface == NULL) { + NORM_ERR("SDL_SetVideoMode: %s", SDL_GetError()); + return false; + } + draw_stuff(); /* redraw everything in our newly sized window */ + + /* swap buffers */ + if (surface && (surface->flags & SDL_HWSURFACE)) SDL_Flip(surface); + SDL_UpdateRect(surface, 0, 0, surface->w, surface->h); + + changed++; + /* update lua window globals */ + llua_update_window_table(text_start_x, text_start_y, text_width, + text_height); + } + + clear_text(1); + } + + /* handle events */ + while (SDL_PollEvent(&ev) != 0) { + //printf("ev %d\n", ev.type); + + switch (ev.type) { + case SDL_ACTIVEEVENT: + need_to_update = 1; + printf("gap: %d x %d\n", gap_x.get(*state), gap_y.get(*state)); + break; + case SDL_VIDEORESIZE: + // TODO + break; + case SDL_QUIT: + // FIXME + g_sigterm_pending = 1; + break; + case SDL_USEREVENT: + /* currently only the timeout */ + update_text(); + draw_stuff(); + SDL_UpdateRect(surface, 0, 0, surface->w, surface->h); + //need_to_update = 1; + break; + default: + break; + } + } + + // if (surface && (surface->flags & SDL_HWSURFACE)) + // TODO: clear back buffer? + + // handled + return true; +} + +void display_output_sdl::sigterm_cleanup() {} + +void display_output_sdl::cleanup() { + // XXX:shutdown(); + free_fonts(utf8_mode.get(*state)); +} + +void display_output_sdl::set_foreground_color(long c) { + // TODO +} + +int display_output_sdl::calc_text_width(const char *s) { + size_t slen = strlen(s); + // TODO + // return XTextWidth(x_fonts[selected_font].font, s, slen); + int w, h; + // TODO: check utf8_mode.get(*state) ? + if (sdl_fonts[thefont].font == nullptr || TTF_SizeUTF8(sdl_fonts[thefont].font, s, &w, &h) < 0) + return slen * 10; + return w; +} + +void display_output_sdl::draw_string_at(int x, int y, const char *s, int w) { + if (sdl_fonts[thefont].font == nullptr) + return; + SDL_Color c = { 255, 0, 0 }; + SDL_Surface *text = TTF_RenderText_Blended(sdl_fonts[thefont].font, s, c); + y -= text->h; + SDL_Rect sr = { 0, 0, (Uint16)(text->w), (Uint16)(text->h)}; + SDL_Rect dr = { (Sint16)x, (Sint16)y, (Uint16)(text->w), (Uint16)(text->h)}; + SDL_BlitSurface(text, &sr, surface, &dr); + SDL_FreeSurface(text); + // TODO +} + +void display_output_sdl::set_line_style(int w, bool solid) { + // TODO +} + +void display_output_sdl::set_dashes(char *s) { + // TODO +} + +void display_output_sdl::draw_line(int x1, int y1, int x2, int y2) { + // TODO +} + +void display_output_sdl::draw_rect(int x, int y, int w, int h) { + printf("draw_rect(%d, %d, %d, %d)\n", x, y, w, h); + SDL_Rect r = {(Sint16)x, (Sint16)y, (Uint16)w, 1}; + // TODO + SDL_FillRect(surface, &r, SDL_MapRGB(surface->format, 255, 255, 255)); + r.y += h/* - 1*/; + SDL_FillRect(surface, &r, SDL_MapRGB(surface->format, 255, 255, 255)); + r.y -= h/* - 1*/; + r.h = h + 1; + r.w = 1; + SDL_FillRect(surface, &r, SDL_MapRGB(surface->format, 255, 255, 255)); + r.x += w/* - 1*/; + SDL_FillRect(surface, &r, SDL_MapRGB(surface->format, 255, 255, 255)); +} + +void display_output_sdl::fill_rect(int x, int y, int w, int h) { + printf("fill_rect(%d, %d, %d, %d)\n", x, y, w, h); + SDL_Rect r = {(Sint16)x, (Sint16)y, (Uint16)w, (Uint16)h}; + // TODO: fill with fg color + SDL_FillRect(surface, &r, SDL_MapRGB(surface->format, 255, 255, 255)); +} + +void display_output_sdl::draw_arc(int x, int y, int w, int h, int a1, int a2) { + // TODO +} + +void display_output_sdl::move_win(int x, int y) { + /* not possible in SDL 1.2 */ +} + +int display_output_sdl::dpi_scale(int value) { return value; } + +void display_output_sdl::end_draw_stuff() { + if (surface && (surface->flags & SDL_HWSURFACE)) SDL_Flip(surface); +} + +void display_output_sdl::clear_text(int exposures) { + if (surface != nullptr) { + /* there is some extra space for borders and outlines */ + int border_total = get_border_total(); + + SDL_Rect r = {(Sint16)(text_start_x - border_total), + (Sint16)(text_start_y - border_total), + (Uint16)(text_width + 2 * border_total), + (Uint16)(text_height + 2 * border_total)}; + // TODO: fill with bg color + SDL_FillRect(surface, &r, SDL_MapRGB(surface->format, 0, 0, 0)); + } +} + +int display_output_sdl::font_height(unsigned int f) { + assert(f < sdl_fonts.size()); + // return sdl_fonts[f].font->max_bounds.ascent + + // sdl_fonts[f].font->max_bounds.descent; + if (sdl_fonts[f].font == nullptr) + return 10; + return TTF_FontHeight(sdl_fonts[f].font); +} + +int display_output_sdl::font_ascent(unsigned int f) { + assert(f < sdl_fonts.size()); + if (sdl_fonts[f].font == nullptr) + return 0; + return TTF_FontAscent(sdl_fonts[f].font); + // return x_fonts[f].font->max_bounds.ascent; +} + +int display_output_sdl::font_descent(unsigned int f) { + assert(f < sdl_fonts.size()); + // return sdl_fonts[f].font->max_bounds.descent; + if (sdl_fonts[f].font == nullptr) + return 0; + return std::abs(TTF_FontDescent(sdl_fonts[f].font)); +} + +void display_output_sdl::setup_fonts(void) {} + +void display_output_sdl::set_font(unsigned int f) { + assert(f < sdl_fonts.size()); + thefont = f; +} + +void display_output_sdl::free_fonts(bool utf8) { + for (auto &font : sdl_fonts) { + if (font.font) + TTF_CloseFont(font.font); + } + sdl_fonts.clear(); +} + +/* + * We try to locate TTF files by comparing with the font name, + * after removing spaces and other separator characters. + */ +static const char *font_name_filtered_chars = " _-"; +static std::string searched_font; +static int font_finder(const char *fpath, const struct stat *sb, int typeflag) { + //printf("%s %d\n", fpath, typeflag); + + if (typeflag != FTW_F) + return 0; + + const char *p = strrchr(fpath, DIRSEP_CHAR); + if (p == nullptr) + return 0; + p++; + + std::string name; + for (unsigned int i = 0; p[i]; i++) { + if (strchr(font_name_filtered_chars, p[i])) + continue; + name += p[i]; + } + //printf("%s\n", name.c_str()); + if (name.compare(searched_font)) + return 0; + + searched_font = fpath; + return 1; +} + +void display_output_sdl::load_fonts(bool utf8) { + sdl_fonts.resize(fonts.size()); + + // TODO: should we cache them? + + const char *font_search_paths[] = { + // TODO: depends on the OS + // maybe use XDG_DATA_DIRS + // also check local user dirs + "/usr/share/fonts", + nullptr + }; + + for (unsigned int f = 0; f < fonts.size(); f++) { + auto &font = fonts[f]; + auto &sdlfont = sdl_fonts[f]; + sdlfont.font = nullptr; + const char *p, *q; + int size = 1; + + searched_font = ""; + p = font.name.c_str(); + q = strstr(p, ":size="); + if (q) + size = (int)strtol(q + 6, nullptr, 10); + + for (unsigned int i = 0; p[i] && (&p[i] != q); i++) { + if (strchr(font_name_filtered_chars, p[i])) + continue; + searched_font += p[i]; + } + searched_font += ".ttf"; + +#ifdef HAVE_FTW_H + int found = 0; + for (unsigned int i = 0; font_search_paths[i]; i++) { + found = ftw(font_search_paths[i], &font_finder, 20); + printf("err %d\nF: %s\n", found, searched_font.c_str()); + if (found == 1) + break; + } +#else +#error WRITEME +#endif + if (found == 1) { + sdlfont.font = TTF_OpenFont(searched_font.c_str(), size); + } else { + NORM_ERR("can't load font '%s' %d", searched_font.c_str(), size); + } + } +} + +#endif /* BUILD_SDL */ + +} // namespace conky diff --git a/src/display-sdl.hh b/src/display-sdl.hh new file mode 100644 index 0000000000..1539a6ea4e --- /dev/null +++ b/src/display-sdl.hh @@ -0,0 +1,86 @@ +/* + * + * Conky, a system monitor, based on torsmo + * + * Please see COPYING for details + * + * Copyright (C) 2022 François Revol et al. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef DISPLAY_SDL_HH +#define DISPLAY_SDL_HH + +#include +#include +#include + +#include "display-output.hh" +#include "luamm.hh" + +namespace conky { + +/* + * A base class for SDL display output. + */ +class display_output_sdl : public display_output_base { + public: + explicit display_output_sdl(); + + virtual ~display_output_sdl() {} + + // check if available and enabled in settings + virtual bool detect(); + // connect to DISPLAY and other stuff + virtual bool initialize(); + virtual bool shutdown(); + + virtual bool main_loop_wait(double); + + virtual void sigterm_cleanup(); + virtual void cleanup(); + + // drawing primitives + virtual void set_foreground_color(long); + + virtual int calc_text_width(const char *); + + // GUI interface + virtual void draw_string_at(int, int, const char *, int); + // X11 lookalikes + virtual void set_line_style(int, bool); + virtual void set_dashes(char *); + virtual void draw_line(int, int, int, int); + virtual void draw_rect(int, int, int, int); + virtual void fill_rect(int, int, int, int); + virtual void draw_arc(int, int, int, int, int, int); + virtual void move_win(int, int); + virtual int dpi_scale(int); + + virtual void end_draw_stuff(); + virtual void clear_text(int); + + virtual int font_height(unsigned int); + virtual int font_ascent(unsigned int); + virtual int font_descent(unsigned int); + virtual void setup_fonts(void); + virtual void set_font(unsigned int); + virtual void free_fonts(bool); + virtual void load_fonts(bool); +}; + +} // namespace conky + +#endif /* DISPLAY_SDL_HH */