diff --git a/cmake/ConkyBuildOptions.cmake b/cmake/ConkyBuildOptions.cmake index d70af734b8..9c926f5c1f 100644 --- a/cmake/ConkyBuildOptions.cmake +++ b/cmake/ConkyBuildOptions.cmake @@ -203,10 +203,14 @@ 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) +if(BUILD_X11 OR BUILD_SDL) set(BUILD_GUI true) -endif(BUILD_X11) +endif(BUILD_X11 OR 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 7eea262818..8f8b42fe2e 100644 --- a/cmake/ConkyPlatformChecks.cmake +++ b/cmake/ConkyPlatformChecks.cmake @@ -393,6 +393,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 39a48dd12b..180fd97591 100644 --- a/cmake/config.h.in +++ b/cmake/config.h.in @@ -111,6 +111,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 4544b48fa8..37ba8c4634 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -118,6 +118,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 lua-config.cc diff --git a/src/display-output.cc b/src/display-output.cc index a06e5e028b..f93075e822 100644 --- a/src/display-output.cc +++ b/src/display-output.cc @@ -56,6 +56,7 @@ extern void init_ncurses_output(); extern void init_file_output(); extern void init_http_output(); extern void init_x11_output(); +extern void init_sdl_output(); /* * The selected and active display output. @@ -111,6 +112,7 @@ bool initialize_display_outputs() { init_file_output(); init_http_output(); init_x11_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..bbb03c1b33 --- /dev/null +++ b/src/display-sdl.cc @@ -0,0 +1,548 @@ +/* + * + * 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 "llua.h" +//#include "x11.h" +#ifdef BUILD_SDL +#include "fonts.h" +#endif + +/* TODO: cleanup global namespace */ +#ifdef BUILD_SDL + +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; +TTF_Font *thefont; //FIXME + +/* for sdl_fonts */ +struct sdl_font_list { +#if 0 + XFontStruct *font; + XFontSet fontset; + + sdl_font_list() + : font(nullptr), + fontset(nullptr) + { + } +#endif +}; + +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(); + thefont = TTF_OpenFont("/usr/share/fonts/truetype/noto/NotoMono-Regular.ttf", 12); + + 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_CloseFont(thefont); + 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) { +#if 0 + case Expose: { + XRectangle r; + r.x = ev.xexpose.x; + r.y = ev.xexpose.y; + r.width = ev.xexpose.width; + r.height = ev.xexpose.height; + XUnionRectWithRegion(&r, sdl_stuff.region, sdl_stuff.region); + XSync(display, False); + break; + } + + case PropertyNotify: { + if (ev.xproperty.state == PropertyNewValue) { + get_sdl_desktop_info(ev.xproperty.display, ev.xproperty.atom); + } +#ifdef USE_ARGB + if (!have_argb_visual) { +#endif + if (ev.xproperty.atom == ATOM(_XROOTPMAP_ID) || + ev.xproperty.atom == ATOM(_XROOTMAP_ID)) { + if (forced_redraw.get(*state)) { + draw_stuff(); + next_update_time = get_time(); + need_to_update = 1; + } + } +#ifdef USE_ARGB + } +#endif + break; + } + +#ifdef OWN_WINDOW + case ReparentNotify: + /* make background transparent */ + if (own_window.get(*state)) { + set_transparent_background(window.window); + } + break; + + case ConfigureNotify: + if (own_window.get(*state)) { + /* if window size isn't what expected, set fixed size */ + if (ev.xconfigure.width != window.width || + ev.xconfigure.height != window.height) { + if (window.width != 0 && window.height != 0) { fixed_size = 1; } + + /* clear old stuff before screwing up + * size and pos */ + clear_text(1); + + { + XWindowAttributes attrs; + if (XGetWindowAttributes(display, window.window, &attrs) != 0) { + window.width = attrs.width; + window.height = attrs.height; + } + } + + int border_total = get_border_total(); + + text_width = window.width - 2 * border_total; + text_height = window.height - 2 * border_total; + int mw = this->dpi_scale(maximum_width.get(*state)); + if (text_width > mw && mw > 0) { text_width = mw; } + } + + /* if position isn't what expected, set fixed pos + * total_updates avoids setting fixed_pos when window + * is set to weird locations when started */ + /* // this is broken + if (total_updates >= 2 && !fixed_pos + && (window.x != ev.xconfigure.x + || window.y != ev.xconfigure.y) + && (ev.xconfigure.x != 0 + || ev.xconfigure.y != 0)) { + fixed_pos = 1; + } */ + } + break; + + case ButtonPress: + if (own_window.get(*state)) { + /* if an ordinary window with decorations */ + if ((own_window_type.get(*state) == TYPE_NORMAL && + !TEST_HINT(own_window_hints.get(*state), HINT_UNDECORATED)) || + own_window_type.get(*state) == TYPE_DESKTOP) { + /* allow conky to hold input focus. */ + break; + } + /* forward the click to the desktop window */ + XUngrabPointer(display, ev.xbutton.time); + ev.xbutton.window = window.desktop; + ev.xbutton.x = ev.xbutton.x_root; + ev.xbutton.y = ev.xbutton.y_root; + XSendEvent(display, ev.xbutton.window, False, ButtonPressMask, &ev); + XSetInputFocus(display, ev.xbutton.window, RevertToParent, + ev.xbutton.time); + } + break; + + case ButtonRelease: + if (own_window.get(*state)) { + /* if an ordinary window with decorations */ + if ((own_window_type.get(*state) == TYPE_NORMAL) && + !TEST_HINT(own_window_hints.get(*state), HINT_UNDECORATED)) { + /* allow conky to hold input focus. */ + break; + } + /* forward the release to the desktop window */ + ev.xbutton.window = window.desktop; + ev.xbutton.x = ev.xbutton.x_root; + ev.xbutton.y = ev.xbutton.y_root; + XSendEvent(display, ev.xbutton.window, False, ButtonReleaseMask, &ev); + } + break; + +#endif +#endif // 0 + 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 (thefont == nullptr || TTF_SizeUTF8(thefont, 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 (thefont == nullptr) + return; + SDL_Color c = { 255, 0, 0 }; + SDL_Surface *text = TTF_RenderText_Blended(thefont, 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; + 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 (thefont == nullptr) + return 10; + return TTF_FontHeight(thefont); +} + +int display_output_sdl::font_ascent(unsigned int f) { + assert(f < sdl_fonts.size()); + if (thefont == nullptr) + return 0; + return TTF_FontAscent(thefont); + // 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 (thefont == nullptr) + return 0; + return std::abs(TTF_FontDescent(thefont)); +} + +void display_output_sdl::setup_fonts(void) {} + +void display_output_sdl::set_font(unsigned int f) { + assert(f < sdl_fonts.size()); +} + +void display_output_sdl::free_fonts(bool utf8) { + for (auto &font : sdl_fonts) { + {} + } + sdl_fonts.clear(); +} +void display_output_sdl::load_fonts(bool utf8) { + sdl_fonts.resize(fonts.size()); + for (unsigned int i = 0; i < fonts.size(); i++) { + auto &font = fonts[i]; + auto &sdlfont = sdl_fonts[i]; + } +} + +#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 */