Simple Clock

interpretive language scripts


Moderator: Forum moderators

User avatar
rockedge
Site Admin
Posts: 6553
Joined: Mon Dec 02, 2019 1:38 am
Location: Connecticut,U.S.A.
Has thanked: 2757 times
Been thanked: 2629 times
Contact:

Re: Simple Clock

Post by rockedge »

I compiled the fixed sClockx.c code . Runs but can't be shutdown easily. Needed to pkill sClockx which worked.

Screenshot(21).jpg
Screenshot(21).jpg (26.27 KiB) Viewed 1825 times
User avatar
misko_2083
Posts: 196
Joined: Wed Dec 09, 2020 11:59 pm
Has thanked: 10 times
Been thanked: 20 times

Re: Simple Clock

Post by misko_2083 »

rockedge wrote: Thu Jun 30, 2022 2:06 am

I compiled the fixed sClockx.c code . Runs but can't be shutdown easily. Needed to pkill sClockx which worked.

Screenshot(21).jpg

Here it shuts down when right clicked.
Did the left click change the clock?

Do you want to exit the Circus? The Harsh Truth
https://www.youtube.com/watch?v=ZJwQicZHp_c

Burunduk
Posts: 256
Joined: Thu Jun 16, 2022 6:16 pm
Has thanked: 7 times
Been thanked: 127 times

Re: Simple Clock

Post by Burunduk »

Tried it on Fossapup. Occasionally it reacts on mouse but most often it doesn't. I don't know c and simply removed ret > 0 because it's almost always 0. It works now but maybe I messed up something else that way.

Code: Select all

      if (XPending(disp)) {
          XNextEvent(disp, &ev);
          if (XFilterEvent(&ev, w))
              continue;
          else {
          ...
          }
      }
User avatar
rockedge
Site Admin
Posts: 6553
Joined: Mon Dec 02, 2019 1:38 am
Location: Connecticut,U.S.A.
Has thanked: 2757 times
Been thanked: 2629 times
Contact:

Re: Simple Clock

Post by rockedge »

@misko_2083 No reactions from mouse clicks left or right. Pressing and holding the Alt key I can move it with the mouse

UPDATE: I compiled this version from misko_2083 in KLV-Airedale and this sClockx will shutdown on a right mouse click and changes format and color on a right click

sClockx.c

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <cairo/cairo.h>
#include <cairo/cairo-xlib.h>

/*  gcc sclockx.c -o sclockx $(pkg-config --cflags --libs cairo-xlib-xrender x11)  */

void draw(cairo_t *g, char *date);

int main (int argc, char *argv[]) {
  Display *disp = XOpenDisplay(NULL);
  if(disp == NULL) {
      printf("Display open failed.\n");
      return 1;
  }
  cairo_t *g, *bg;
  cairo_surface_t *ximg,*img;
  int s;

  img = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,180,26);
  bg = cairo_create(img);
  s = DefaultScreen(disp);

  XVisualInfo vinfo;
  XMatchVisualInfo(disp, DefaultScreen(disp), 32, TrueColor, &vinfo);

  XSetWindowAttributes attr;
  attr.override_redirect = False;
  attr.event_mask = ExposureMask | KeyPressMask |
                    VisibilityChangeMask | ButtonPressMask;
  attr.colormap = XCreateColormap(disp, DefaultRootWindow(disp), vinfo.visual, AllocNone);
  attr.border_pixel = 0;
  attr.background_pixel = 0;

  int w =  XCreateWindow(disp, DefaultRootWindow(disp), 0, 0, 180, 26, 0,
                         vinfo.depth, InputOutput, vinfo.visual,
                         CWEventMask | CWColormap | CWBorderPixel | CWBackPixel , &attr);
  XStoreName(disp, w, "sclockx");
  XSelectInput(disp, w, 
               ExposureMask | KeyPressMask | ClientMessage | 
               ButtonPressMask | ButtonReleaseMask);
  XSizeHints sh;
  sh.width = sh.min_width = 150;
  sh.height = sh.min_height = 24;
  sh.max_width = 220;
  sh.max_height = 40;
  sh.win_gravity = 0;
  sh.flags = PSize | PMinSize | PMaxSize | PWinGravity;
  XSetWMNormalHints(disp, w, &sh);

  Atom motif_hints;
  long hints[5] = {2, 0, 0, 0, 0};
  motif_hints = XInternAtom(disp, "_MOTIF_WM_HINTS", False);
  XChangeProperty(disp, w, motif_hints, motif_hints, 32, PropModeReplace, (unsigned char *)&hints, 5);

  Atom window_state = XInternAtom(disp, "_NET_WM_STATE", False);
  long wmStateBelow = XInternAtom(disp, "_NET_WM_STATE_BELOW", False);
  long wmStateSticky = XInternAtom(disp, "_NET_WM_STATE_STICKY", False);
  long wmStateSkipTaskbar = XInternAtom(disp, "_NET_WM_STATE_SKIP_TASKBAR", False);
  long wmStateSkipPager = XInternAtom(disp, "_NET_WM_STATE_SKIP_PAGER", False);
  XChangeProperty(disp, w, window_state, XA_ATOM, 32, PropModeReplace, (unsigned char *) &wmStateBelow, 1);
  XChangeProperty(disp, w, window_state, XA_ATOM, 32, PropModeAppend, (unsigned char *) &wmStateSticky, 1);
  XChangeProperty(disp, w, window_state, XA_ATOM, 32, PropModeAppend, (unsigned char *) &wmStateSkipTaskbar, 1);
  XChangeProperty(disp, w, window_state, XA_ATOM, 32, PropModeAppend, (unsigned char *) &wmStateSkipPager, 1);

  XFlush(disp);
  XMapWindow(disp,w);

  Atom wm_delete_window = XInternAtom(disp,"WM_DELETE_WINDOW",False);
  XSetWMProtocols(disp,w,&wm_delete_window,1);

  ximg = cairo_xlib_surface_create(disp,w,vinfo.visual,180,26);
  cairo_xlib_surface_set_size(ximg,180,26);
  g = cairo_create(ximg);
  cairo_set_source_surface(g,img,0,0);

  int x11_fd;
  fd_set in_fds;
  int nfds = x11_fd + 1;
  struct timeval tv;
  XEvent ev;
  x11_fd = ConnectionNumber(disp);
  static char *date = "%r";
  for(;;) {
      FD_ZERO(&in_fds);
      FD_SET(x11_fd, &in_fds);
      // Set timer to one second
      tv.tv_usec = 0;
      tv.tv_sec = 1;
      // Wait for X Event or a Timer
      int ret;
      if (!XPending(disp))
          ret = select(nfds, &in_fds, NULL, NULL, &tv);
      if (ret == -1 && errno == EINTR)
          continue;
      if (ret == -1)
          goto shutdown;
      if (ret == 0)
          draw(g, date);

      if (XPending(disp)) {
          XNextEvent(disp, &ev);
          if (XFilterEvent(&ev, w))
              continue;
          if (ret > 0) {
              switch (ev.type) {
              case ClientMessage:
              if (ev.xclient.data.l[0] == wm_delete_window) 
                  goto shutdown;
                  case ButtonPress:
                      switch(ev.xbutton.button){
                      case Button1:
                      if (date == "%r")
                          date = "%R:%S";
                      else if (date == "%R:%S")
                          date = "%a %d %b";
                      else if (date == "%a %d %b")
                          date = "      %Y";
                      else
                          date = "%r";
                      continue;
                      case Button3:
                           goto shutdown;
                      }
              default:
                  break;
              }
          }
          else if (ret == 0) 
              draw(g, date);
          else
              printf("An error occured!\n");
      }
  }
  shutdown: printf("Exit\n");
  cairo_destroy(g);
  cairo_surface_destroy(ximg);
  cairo_destroy(bg);
  cairo_surface_destroy(img);
  XDestroyWindow(disp,w);
  XCloseDisplay(disp);
  return 1;
}


void draw(cairo_t *g, char *date) {
  char buffer[64];
  time_t timer;

  struct tm* tm_info;
  time (&timer);
  tm_info = localtime (&timer);
  strftime (buffer, 64, date, tm_info);

  cairo_save (g);
  cairo_set_source_rgba (g,0,0,0,1);
  cairo_set_operator (g, CAIRO_OPERATOR_CLEAR);
  cairo_paint (g);
  cairo_restore (g);
  cairo_set_operator (g, CAIRO_OPERATOR_OVER);
  cairo_save (g);

  if (date == "%r")
      cairo_set_source_rgb (g, 0.0, 1.0, 0.0);
  else if (date == "%R:%S")
      cairo_set_source_rgb (g, 1.0, 0.0, 0.0);
  else if (date == "%a %d %b")
      cairo_set_source_rgb (g, 1.0, 1.0, 1.0);
  else
      cairo_set_source_rgb (g, 0.9, 0.7, 0.0);

  cairo_select_font_face(g,"Sans",CAIRO_FONT_SLANT_NORMAL,CAIRO_FONT_WEIGHT_BOLD);
  cairo_set_font_size(g,24);
  cairo_move_to (g, 0.0, 20.0);
  cairo_show_text(g,buffer);
  cairo_restore (g);
  cairo_paint (g);
}

On the KLV-Airedale attempted to restart this sClockx and it now goes straight to Exit when checked on the command line

Screenshot_2022-06-30_17-08-48.png
Screenshot_2022-06-30_17-08-48.png (202.85 KiB) Viewed 1744 times
User avatar
misko_2083
Posts: 196
Joined: Wed Dec 09, 2020 11:59 pm
Has thanked: 10 times
Been thanked: 20 times

Re: Simple Clock

Post by misko_2083 »

Burunduk wrote: Thu Jun 30, 2022 6:30 pm

Tried it on Fossapup. Occasionally it reacts on mouse but most often it doesn't. I don't know c and simply removed ret > 0 because it's almost always 0. It works now but maybe I messed up something else that way.

Code: Select all

      if (XPending(disp)) {
          XNextEvent(disp, &ev);
          if (XFilterEvent(&ev, w))
              continue;
          else {
          ...
          }
      }

Thank for the input. That's it, it changes to 1only after click.
Image
Everything works for me here for all the versions.

Might as well call draw on click to change it instantly.

Code: Select all

...
                      else
                          date = "%r";
                      draw(g, date);
                      continue;
                      case Button3:
                           goto shutdown;
                           ...

The full code. I hope it works for everyone.

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <cairo/cairo.h>
#include <cairo/cairo-xlib.h>

/*  gcc sclockx.c -o sclockx $(pkg-config --cflags --libs cairo-xlib-xrender x11)  */

void draw(cairo_t *g, char *date);

int main (int argc, char *argv[]) {
  Display *disp = XOpenDisplay(NULL);
  if(disp == NULL) {
      printf("Display open failed.\n");
      return 1;
  }
  cairo_t *g, *bg;
  cairo_surface_t *ximg,*img;
  int s;

  img = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,180,26);
  bg = cairo_create(img);

  XVisualInfo vinfo;
  const char depths[] = { 32, 24, 8, 4, 2, 1, 0 };
  for (int i = 0; depths[i]; i++) {
      XMatchVisualInfo(disp, DefaultScreen(disp), 32, TrueColor, &vinfo);
      if (vinfo.visual)
          break;
  }

  XSetWindowAttributes attr;
  attr.override_redirect = False;
  attr.event_mask = ExposureMask | KeyPressMask |
                    VisibilityChangeMask | ButtonPressMask;
  attr.colormap = XCreateColormap(disp, DefaultRootWindow(disp), vinfo.visual, AllocNone);
  attr.border_pixel = 0;
  attr.background_pixel = 0;

  int w =  XCreateWindow(disp, DefaultRootWindow(disp), 0, 0, 180, 26, 0,
                         vinfo.depth, InputOutput, vinfo.visual,
                         CWEventMask | CWColormap | CWBorderPixel | CWBackPixel , &attr);
  XStoreName(disp, w, "sclockx");
  XSelectInput(disp, w, 
               ExposureMask | KeyPressMask | ClientMessage | 
               ButtonPressMask | ButtonReleaseMask);
  XSizeHints sh;
  sh.width = sh.min_width = 150;
  sh.height = sh.min_height = 24;
  sh.max_width = 220;
  sh.max_height = 40;
  sh.win_gravity = 0;
  sh.flags = PSize | PMinSize | PMaxSize | PWinGravity;
  XSetWMNormalHints(disp, w, &sh);

  Atom motif_hints;
  long hints[5] = {2, 0, 0, 0, 0};
  motif_hints = XInternAtom(disp, "_MOTIF_WM_HINTS", False);
  XChangeProperty(disp, w, motif_hints, motif_hints, 32, PropModeReplace, (unsigned char *)&hints, 5);

  Atom window_state = XInternAtom(disp, "_NET_WM_STATE", False);
  long wmStateBelow = XInternAtom(disp, "_NET_WM_STATE_BELOW", False);
  long wmStateSticky = XInternAtom(disp, "_NET_WM_STATE_STICKY", False);
  long wmStateSkipTaskbar = XInternAtom(disp, "_NET_WM_STATE_SKIP_TASKBAR", False);
  long wmStateSkipPager = XInternAtom(disp, "_NET_WM_STATE_SKIP_PAGER", False);
  XChangeProperty(disp, w, window_state, XA_ATOM, 32, PropModeReplace, (unsigned char *) &wmStateBelow, 1);
  XChangeProperty(disp, w, window_state, XA_ATOM, 32, PropModeAppend, (unsigned char *) &wmStateSticky, 1);
  XChangeProperty(disp, w, window_state, XA_ATOM, 32, PropModeAppend, (unsigned char *) &wmStateSkipTaskbar, 1);
  XChangeProperty(disp, w, window_state, XA_ATOM, 32, PropModeAppend, (unsigned char *) &wmStateSkipPager, 1);

  XFlush(disp);
  XMapWindow(disp,w);

  Atom wm_delete_window = XInternAtom(disp,"WM_DELETE_WINDOW",False);
  XSetWMProtocols(disp,w,&wm_delete_window,1);

  ximg = cairo_xlib_surface_create(disp,w,vinfo.visual,180,26);
  cairo_xlib_surface_set_size(ximg,180,26);
  g = cairo_create(ximg);
  cairo_set_source_surface(g,img,0,0);

  int x11_fd;
  fd_set in_fds;
  int nfds = x11_fd + 1;
  struct timeval tv;
  XEvent ev;
  x11_fd = ConnectionNumber(disp);
  static char *date = "%r";
  for(;;) {
      FD_ZERO(&in_fds);
      FD_SET(x11_fd, &in_fds);
      // Set timer to one second
      tv.tv_usec = 0;
      tv.tv_sec = 1;

      // Wait for X Event or a Timer
      int ret;
      if (!XPending(disp))
          ret = select(nfds, &in_fds, NULL, NULL, &tv);
      if (ret == -1 && errno == EINTR)
          continue;
      if (ret == -1)
          goto shutdown;
      if (ret == 0)
          draw(g, date);

      if (XPending(disp)) {
          XNextEvent(disp, &ev);
          if (XFilterEvent(&ev, w))
              continue;
          switch (ev.type) {
              case ClientMessage:
                  if (ev.xclient.data.l[0] == wm_delete_window) 
                      goto shutdown;
              case ButtonPress:
                  switch(ev.xbutton.button){
                      case Button1:
                      if (date == "%r")
                          date = "%R:%S";
                      else if (date == "%R:%S")
                          date = "%a %d %b";
                      else if (date == "%a %d %b")
                          date = "      %Y";
                      else
                          date = "%r";
                      draw(g, date);
                      continue;
                      case Button3:
                           goto shutdown;
             default:
                  break;
              }
          }
      }
      if (ret > 1)
          printf("An error occured!\n");
  }
  shutdown: printf("Exit\n");
  cairo_destroy(g);
  cairo_surface_destroy(ximg);
  cairo_destroy(bg);
  cairo_surface_destroy(img);
  XDestroyWindow(disp,w);
  XCloseDisplay(disp);
  return 0;
}

void draw(cairo_t *g, char *date) {
  char buffer[64];
  time_t timer;

  struct tm* tm_info;
  time (&timer);
  tm_info = localtime (&timer);
  strftime (buffer, 64, date, tm_info);

  cairo_save (g);
  cairo_set_source_rgba (g,0,0,0,1);
  cairo_set_operator (g, CAIRO_OPERATOR_CLEAR);
  cairo_paint (g);
  cairo_restore (g);
  cairo_set_operator (g, CAIRO_OPERATOR_OVER);
  cairo_save (g);

  if (date == "%r")
      cairo_set_source_rgb (g, 0.0, 1.0, 0.0);
  else if (date == "%R:%S")
      cairo_set_source_rgb (g, 1.0, 0.0, 0.0);
  else if (date == "%a %d %b")
      cairo_set_source_rgb (g, 1.0, 1.0, 1.0);
  else
      cairo_set_source_rgb (g, 0.9, 0.7, 0.0);

  cairo_select_font_face(g,"Sans",CAIRO_FONT_SLANT_NORMAL,CAIRO_FONT_WEIGHT_BOLD);
  cairo_set_font_size(g,24);
  cairo_move_to (g, 0.0, 20.0);
  cairo_show_text(g,buffer);
  cairo_restore (g);
  cairo_paint (g);
}

36°C today

Do you want to exit the Circus? The Harsh Truth
https://www.youtube.com/watch?v=ZJwQicZHp_c

Burunduk
Posts: 256
Joined: Thu Jun 16, 2022 6:16 pm
Has thanked: 7 times
Been thanked: 127 times

Re: Simple Clock

Post by Burunduk »

The value was 0 even after click, this is why I removed the test. After this it reacted on clicks with a 1 sec delay. Your latest version reacts immediately. Thanks.
(Now I can see ret == 1 after click, don't know what makes the difference.)

To move the window to a certain position the wmctrl command can be used:

Code: Select all

# in this example x=250, y=150
wmctrl -r sclockx -e 0,250,150,-1,-1

(My conky shows 10°C higher CPU idle temp than it was in the spring.)

Update: The borders remain visible because picom draws shadows. Its config has this: dnd = { fade = false; shadow = false; } (same for dock but it's special for jwm) So tried to set it:

Code: Select all

  Atom window_type = XInternAtom(disp, "_NET_WM_WINDOW_TYPE", False);
  long wmWindowTypeDnD = XInternAtom(disp, "_NET_WM_WINDOW_TYPE_DND", False);
  XChangeProperty(disp, w, window_type, XA_ATOM, 32, PropModeAppend, (unsigned char *) &wmWindowTypeDnD, 1);

Is there a better way to remove decorations of a specific window?

User avatar
misko_2083
Posts: 196
Joined: Wed Dec 09, 2020 11:59 pm
Has thanked: 10 times
Been thanked: 20 times

Re: Simple Clock

Post by misko_2083 »

Burunduk wrote: Fri Jul 01, 2022 10:12 am

The value was 0 even after click, this is why I removed the test. After this it reacted on clicks with a 1 sec delay. Your latest version reacts immediately. Thanks.
(Now I can see ret == 1 after click, don't know what makes the difference.)

To move the window to a certain position the wmctrl command can be used:

Code: Select all

# in this example x=250, y=150
wmctrl -r sclockx -e 0,250,150,-1,-1

(My conky shows 10°C higher CPU idle temp than it was in the spring.)

Update: The borders remain visible because picom draws shadows. Its config has this: dnd = { fade = false; shadow = false; } (same for dock but it's special for jwm) So tried to set it:

Code: Select all

  Atom window_type = XInternAtom(disp, "_NET_WM_WINDOW_TYPE", False);
  long wmWindowTypeDnD = XInternAtom(disp, "_NET_WM_WINDOW_TYPE_DND", False);
  XChangeProperty(disp, w, window_type, XA_ATOM, 32, PropModeAppend, (unsigned char *) &wmWindowTypeDnD, 1);

Is there a better way to remove decorations of a specific window?

Not all window managers follow freedesktop.org specifications.
Some follow motif wm hints, but maybe SPLASH would do that. https://specifications.freedesktop.org/ ... 01s05.html
if I set _NET_WM_WINDOW_TYPE_SPLASH, Xfwm4 doesn't follow the gravity set in size hints to center.
It isn't placing it in center but to the top left.
I should implement that as an option along with the X and Y positions of the window.

Do you want to exit the Circus? The Harsh Truth
https://www.youtube.com/watch?v=ZJwQicZHp_c

User avatar
rockedge
Site Admin
Posts: 6553
Joined: Mon Dec 02, 2019 1:38 am
Location: Connecticut,U.S.A.
Has thanked: 2757 times
Been thanked: 2629 times
Contact:

Re: Simple Clock

Post by rockedge »

In KLV-Airedale (and Puppy Linux Bionic64) I am using a .desktop file for a menu start option.

Code: Select all

[Desktop Entry]
Encoding=UTF-8
Name=sClock
Icon=/usr/share/pixmaps/clock_digital.svg
Comment=Desktop Clock
Exec=sClock --color=orange --center --stick
Terminal=false
Type=Application
Categories=X-Desktop
GenericName=Desktop clock

Looks like this in KLV-Airedale-beta15 ->
at start up.....

Screenshot_2022-07-02_17-21-53.jpg
Screenshot_2022-07-02_17-21-53.jpg (56.13 KiB) Viewed 1621 times

moved to a better contrast region.....

Screenshot_2022-07-02_17-23-23.jpg
Screenshot_2022-07-02_17-23-23.jpg (56.73 KiB) Viewed 1621 times
User avatar
misko_2083
Posts: 196
Joined: Wed Dec 09, 2020 11:59 pm
Has thanked: 10 times
Been thanked: 20 times

Re: Simple Clock

Post by misko_2083 »

rockedge wrote: Sat Jul 02, 2022 9:22 pm

In KLV-Airedale (and Puppy Linux Bionic64) I am using a .desktop file for a menu start option.

Code: Select all

[Desktop Entry]
Encoding=UTF-8
Name=sClock
Icon=/usr/share/pixmaps/clock_digital.svg
Comment=Desktop Clock
Exec=sClock --color=orange --center --stick
Terminal=false
Type=Application
Categories=X-Desktop
GenericName=Desktop clock

Looks like this in KLV-Airedale-beta15 ->
at start up.....
Screenshot_2022-07-02_17-21-53.jpg
moved to a better contrast region.....
Screenshot_2022-07-02_17-23-23.jpg

sclock (gtk3) fits good in there rockedge.

In the meantime, the new sclockx got some options.

Code: Select all

"  -h, --help        Show help message and quit\n"
"  --above           Keep clock above other windows, default is below\n"
"  --color=          Set text color, e.g. fcfcfc, this option can be set multiple times\n"
"  --posx=           Set X position\n"
"  --posy=           Set Y position\n"
"  --splash          Set window type to splash\n"

Now setting the color of all the clocks looks like:

Code: Select all

sclockx --color="ffffff"  --color f6aa33 --color fcfccc --color ffccff --above

If not set it defaults to first set, and if first color is not set to green..

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <getopt.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <cairo/cairo.h>
#include <cairo/cairo-xlib.h>
#include <sys/select.h>

/*  gcc sclockx.c -o sclockx $(pkg-config --cflags --libs cairo-xlib-xrender x11)  */

struct xwindow {
  Display *disp;
  Window w;
  XVisualInfo vinfo;
  cairo_t *cr;
  cairo_t *bg;
  cairo_surface_t *img;
  cairo_surface_t *ximg;
};

struct options {
  int pos_x;
  int pos_y;
  int above;
  int splash;
  int start;
};

struct colors {
    int rgb[3];
    int set[1];
};

static const char sclockx_usage[] =
"Usage: sclockx [options]\n\n"
"  -h, --help        Show help message and quit\n"
"  --above           Keep clock above other windows, default is below\n"
"  --color=          Set text color, e.g. fcfcfc, this option can be set multiple times\n"
"  --posx=           Set X position\n"
"  --posy=           Set Y position\n"
"  --splash          Set window type to splash\n"
"\n";

static void
draw (cairo_t *g, char *date,
         struct colors *col);

static void
set_source_color (cairo_t *g,
                  struct colors *col,
                  int color_index);

static void
x11_create_window (struct xwindow *ctx);

static void
x11_move_window (struct xwindow *ctx,
                 struct options *opt);
static void
set_cairo (struct xwindow *ctx);

static void
main_loop (struct xwindow *ctx,
           struct colors *col);

static void
set_hints (struct xwindow *ctx,
           struct options *opt);

static char *
set_above (int above);

static void usage (void);

int main (int argc, char **argv)
{
  struct colors col[4];
  struct options opt = { 0 };
  /* initial values */
  opt.pos_x = -1;
  opt.pos_y = -1;
  opt.splash = 0;
  opt.start = 1;
  col[0].rgb[0] = 0;
  col[0].rgb[1] = 255;
  col[0].rgb[2] = 0;

  int c;
  int i, index = 0;
  static struct option long_options[] = {
      {"help",    no_argument,        0, 'h'},
      {"above",   no_argument,        0,  2 },
      {"color",   required_argument,  0,  0 },
      {"posx",    required_argument,  0, 'x'},
      {"posy",    required_argument,  0, 'y'},
      {"splash",  no_argument,        0,  1 },
      {"start",  required_argument,   0, 's'},
      {0,         0,                  0,  0 }
  };

  while ((c = getopt_long(argc, argv, "hx:y:0:12",
                          long_options, NULL)) != -1) {
      switch (c) {
          case 'x':
              if (atoi(optarg) > -1)
                  opt.pos_x = atoi(optarg);
              break;
          case 'y':
              if (atoi(optarg) > -1)
                  opt.pos_y = atoi(optarg);
              break;
          case 0:
              sscanf(optarg, "%02x%02x%02x", &col[index].rgb[0], &col[index].rgb[1], &col[index].rgb[2]);
              for (i = 0; i < 3; i++) {
                  if (col[index].rgb[i] > 255 || col[index].rgb[i] < -1) {
                      printf("Invalid color %s\n", optarg);
                      usage();
                  }
              }
              sscanf("1", "%d", &col[index].set[0]);
              index++;
              if (index > 4)
                  usage();
              break;
          case 1:
              opt.splash = 1;
              break;
          case 2:
              opt.above = 1;
              break;
          case 'h':
          default:
              usage();
      }
  }

  if (optind < argc)
      usage();

  struct xwindow ctx = { 0 };

  ctx.disp = XOpenDisplay(NULL);

  if (!ctx.disp) {
      printf("Display open failed.\n");
      return 1;
  }

  x11_create_window (&ctx);
  set_cairo (&ctx);
  set_hints (&ctx, &opt);

  XMapWindow(ctx.disp, ctx.w);
  x11_move_window (&ctx, &opt);
  XSync (ctx.disp, False);

  main_loop (&ctx, col);

  cairo_destroy (ctx.cr);
  cairo_surface_destroy (ctx.ximg);
  cairo_destroy (ctx.bg);
  cairo_surface_destroy(ctx.img);
  XDestroyWindow (ctx.disp, ctx.w);
  XCloseDisplay (ctx.disp);
  return 0;
}

static void
x11_create_window (struct xwindow *ctx)
{
  const char depths[] = { 32, 24, 8, 4, 2, 1, 0 };
  for (int i = 0; depths[i]; i++) {
      XMatchVisualInfo (ctx->disp,
                        DefaultScreen (ctx->disp),
                        32, TrueColor, &ctx->vinfo);
      if (ctx->vinfo.visual)
          break;
  }
  XSetWindowAttributes attr;
  attr.override_redirect = False;
  attr.event_mask =  KeyPressMask | ButtonPressMask;
  attr.colormap = XCreateColormap(ctx->disp,
                                  DefaultRootWindow (ctx->disp),
                                  ctx->vinfo.visual, AllocNone);
  attr.border_pixel = 0;
  attr.background_pixel = 0;
  ctx->w =  XCreateWindow(ctx->disp, DefaultRootWindow (ctx->disp), 0, 0, 180, 26, 0,
                         ctx->vinfo.depth, InputOutput, ctx->vinfo.visual,
                         CWEventMask | CWColormap | CWBorderPixel | CWBackPixel , &attr);
  XStoreName(ctx->disp, ctx->w, "sclockx");
  XSelectInput(ctx->disp, ctx->w, 
               KeyPressMask | ClientMessage | 
               ButtonPressMask | ButtonReleaseMask);
}

static void
x11_move_window (struct xwindow *ctx,
                 struct options *opt)
{
  if ((opt->pos_x > -1) && (opt->pos_y > -1))
       XMoveWindow (ctx->disp, ctx->w, opt->pos_x, opt->pos_y);
  if ((opt->pos_x > -1) && !(opt->pos_y > -1))
      XMoveWindow (ctx->disp, ctx->w, opt->pos_x, 0);
  if (!(opt->pos_x > -1) && (opt->pos_y > -1))
      XMoveWindow (ctx->disp, ctx->w, 0, opt->pos_y);
}

static void
set_cairo (struct xwindow *ctx)
{
  ctx->img = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 180, 26);
  ctx->bg = cairo_create (ctx->img);
  ctx->ximg = cairo_xlib_surface_create (ctx->disp, ctx->w,
                                    ctx->vinfo.visual, 180, 26);
  cairo_xlib_surface_set_size (ctx->ximg, 180, 26);
  ctx->cr = cairo_create (ctx->ximg);
  cairo_set_source_surface (ctx->cr, ctx->img, 0, 0);
}

static void
main_loop (struct xwindow *ctx,
           struct colors *col)
{
  int x11_fd;
  fd_set in_fds;
  int nfds = x11_fd + 4; //getopts opens something +1
  struct timeval tv = { 0 };
  XEvent ev;
  x11_fd = ConnectionNumber (ctx->disp);
  static char *date = "%r";
  Atom wm_delete_window = XInternAtom (ctx->disp, "WM_DELETE_WINDOW", False);
  XSetWMProtocols (ctx->disp, ctx->w, &wm_delete_window, 1);
  for (;;) {
      FD_ZERO(&in_fds);
      FD_SET(x11_fd, &in_fds);
      // Set timer to one second
      tv.tv_usec = 0;
      tv.tv_sec = 1;

      // Wait for X Event or a Timer
      int ret;
      if (!XPending(ctx->disp))
          ret = select (nfds, &in_fds, NULL, NULL, &tv);
      if (ret == -1 && errno == EINTR)
          continue;
      if (ret == -1)
          goto shutdown;
      if (ret == 0)
          draw(ctx->cr, date, col);

      if (XPending (ctx->disp)) {
          XNextEvent (ctx->disp, &ev);
          if (XFilterEvent (&ev, ctx->w))
              continue;
          switch (ev.type) {
              case ClientMessage:
                  if (ev.xclient.data.l[0] == wm_delete_window) 
                      goto shutdown;
              case ButtonPress:
                  switch(ev.xbutton.button){
                      case Button1:
                      if (date == "%r")
                          date = "%R:%S";
                      else if (date == "%R:%S")
                          date = "%a %d %b";
                      else if (date == "%a %d %b")
                          date = "      %Y";
                      else
                          date = "%r";
                      draw(ctx->cr, date, col);
                      continue;
                      case Button3:
                           goto shutdown;
                      default:
                          break;
              }
          }
      }
      if (ret > 1)
          printf("An error occured!%d\n", ret);
  }
  shutdown: printf("Exit\n");
}

static void
draw (cairo_t *g, char *date,
      struct colors *col)
{
  char buffer[12];
  time_t timer;

  struct tm* tm_info;
  time (&timer);
  tm_info = localtime (&timer);
  strftime (buffer, 64, date, tm_info);

  cairo_save (g);
  cairo_set_source_rgba (g,0,0,0,1);
  cairo_set_operator (g, CAIRO_OPERATOR_CLEAR);
  cairo_paint (g);
  cairo_restore (g);

  cairo_set_operator (g, CAIRO_OPERATOR_OVER);
  cairo_save (g);
  if (date == "%r")
      set_source_color(g, col, 0);
  else if (date == "%R:%S")
      set_source_color(g, col, 1);
  else if (date == "%a %d %b")
      set_source_color(g, col, 2);
  else
      set_source_color(g, col, 3);

  cairo_select_font_face(g,"Sans",CAIRO_FONT_SLANT_NORMAL,CAIRO_FONT_WEIGHT_BOLD);
  cairo_set_font_size(g,24);
  cairo_move_to (g, 0.0, 20.0);
  cairo_show_text(g,buffer);
  cairo_restore (g);
  cairo_paint (g);
}

static void
set_source_color (cairo_t *g, struct colors *col, int color_index)
{

 if (col[color_index].set[0] != 1)
     color_index = 0;
 cairo_set_source_rgb (g, (float) col[color_index].rgb[0] / 255,
                          (float) col[color_index].rgb[1] / 255,
                          (float) col[color_index].rgb[2] / 255);
}

static void
set_hints (struct xwindow *ctx,
           struct options *opt)
{
  XSizeHints sh;
  sh.width = sh.min_width = 150;
  sh.height = sh.min_height = 24;
  sh.max_width = 220;
  sh.max_height = 40;
  sh.win_gravity = 0;
  sh.flags = PSize | PMinSize | PMaxSize | PWinGravity;
  XSetWMNormalHints (ctx->disp, ctx->w, &sh);

  Atom motif_hints;
  long hints[5] = {2, 0, 0, 0, 0};
  motif_hints = XInternAtom(ctx->disp, "_MOTIF_WM_HINTS", False);
  XChangeProperty (ctx->disp, ctx->w, motif_hints, motif_hints, 32,
                   PropModeReplace, (unsigned char *)&hints, 5);

  Atom window_state = XInternAtom (ctx->disp, "_NET_WM_STATE", False);

  long wmStateBelow = XInternAtom (ctx->disp, set_above(opt->above), False);
  long wmStateSticky = XInternAtom (ctx->disp, "_NET_WM_STATE_STICKY", False);
  long wmStateSkipTaskbar = XInternAtom (ctx->disp, "_NET_WM_STATE_SKIP_TASKBAR", False);
  long wmStateSkipPager = XInternAtom (ctx->disp, "_NET_WM_STATE_SKIP_PAGER", False);
  XChangeProperty (ctx->disp, ctx->w, window_state, XA_ATOM, 32,
                   PropModeReplace, (unsigned char *) &wmStateBelow, 1);
  XChangeProperty (ctx->disp, ctx->w, window_state, XA_ATOM, 32,
                   PropModeAppend, (unsigned char *) &wmStateSticky, 1);
  XChangeProperty (ctx->disp, ctx->w, window_state, XA_ATOM, 32,
                   PropModeAppend, (unsigned char *) &wmStateSkipTaskbar, 1);
  XChangeProperty (ctx->disp, ctx->w, window_state, XA_ATOM, 32,
                   PropModeAppend, (unsigned char *) &wmStateSkipPager, 1);

  if (opt->splash) {
      Atom window_type = XInternAtom (ctx->disp, "_NET_WM_WINDOW_TYPE", False);
      long wmWindowTypeSplash = XInternAtom (ctx->disp, "_NET_WM_WINDOW_TYPE_SPLASH", False);
      XChangeProperty (ctx->disp, ctx->w, window_type, XA_ATOM, 32,
                       PropModeAppend, (unsigned char *) &wmWindowTypeSplash, 1);
  }
}

static char *
set_above (int above)
{
  static char *ret;
  if (above)
      ret = "_NET_WM_STATE_ABOVE";
  else
      ret = "_NET_WM_STATE_BELOW";
  return ret;
}

static void
usage(void)
{
	printf("%s", sclockx_usage);
	exit(0);
}

What surprised me the most is that getopts opens some fd and leaves them open.
That interfered with the timeout and made the app extremely slow.
Had to change this:

Code: Select all

  int nfds = x11_fd + 1;

Once I figured this, everything else was easy.

Code: Select all

  int nfds = x11_fd + 4; //getopts opens something +1

Except half an hour power outage today, of course.
But I saved the code just before.

Do you want to exit the Circus? The Harsh Truth
https://www.youtube.com/watch?v=ZJwQicZHp_c

masinick
Posts: 6
Joined: Mon Jun 13, 2022 6:37 pm
Has thanked: 1 time

Re: Simple Clock

Post by masinick »

Wow! I'm glad that many of you got involved in writing some tools and scripts.

I found good examples long ago; the past 3-4 pages are well beyond but I was originally looking for, but as I say, if other participants want to embellish and enhance the scripts and write programs, this is precisely what "freedom" in software is all about. Congratulations to all of you; even the more "experienced" among us learned a few things along the way; I certainly learned what I was seeking, and now I have a small collection of scripts from a couple of lines to perhaps half a screen in size.

I also tried a few of the programs, though these are beyond the original scope of what I was looking for; nevertheless, this entire conversation was quite interesting and educational! Thank you all!

User avatar
rockedge
Site Admin
Posts: 6553
Joined: Mon Dec 02, 2019 1:38 am
Location: Connecticut,U.S.A.
Has thanked: 2757 times
Been thanked: 2629 times
Contact:

Re: Simple Clock

Post by rockedge »

compiled the latest version and a small problem showed up while left clicking->

Code: Select all

root# ./sclockx --color="ffffff"  --color f6aa33 --color fcfccc --color ffccff --above
*** stack smashroot# ./sclockx --color="ffffff"  --color f6aa33 --color fcfccc --color ffccff --above
*** stack smashing detected ***: terminated
Aborted

first left click changes clock format, second left click is causing stack smashing!

Very first time in all of these years I could generate stack smashing error! Didn't even know there was such a beast :o

Should be a name of a Punk Rock or Heavy metal band. "Stack Smash"

Burunduk
Posts: 256
Joined: Thu Jun 16, 2022 6:16 pm
Has thanked: 7 times
Been thanked: 127 times

Re: Simple Clock

Post by Burunduk »

On my Fossapup, strftime() produces strings up to 14 bytes long (15 with \0). The buffer in draw() is too small. char buffer[64]; solves the problem.

Update:

misko_2083 wrote: Mon Jul 04, 2022 4:02 pm

Had to change this:

Code: Select all

  int nfds = x11_fd + 1;

The compiler warns that x11_fd is used uninitialized. Moved it, seems to work normally:

Code: Select all

  x11_fd = ConnectionNumber (ctx->disp);
  int nfds = x11_fd + 1;

Another warning was about "comparison with string literal results in unspecified behavior". While it works because the pointers are compared, it's probably simpler to use an integer here:

Code: Select all

static void
draw (cairo_t *g, int mode,
         struct colors *col);
...
  static int mode = 0;
...
      if (ret == 0)
          draw(ctx->cr, mode, col);

      if (XPending (ctx->disp)) {
          XNextEvent (ctx->disp, &ev);
          if (XFilterEvent (&ev, ctx->w))
              continue;
          switch (ev.type) {
              case ClientMessage:
                  if (ev.xclient.data.l[0] == wm_delete_window) 
                      goto shutdown;
              case ButtonPress:
                  switch(ev.xbutton.button){
                      case Button1:
                      mode = mode == 3 ? 0 : mode + 1;
                      draw(ctx->cr, mode, col);
                      continue;
...
static void
draw (cairo_t *g, int mode,
      struct colors *col)
{
  char buffer[64];
  time_t timer;
  const char *fmt[] = {"%r", "%R:%S", "%a %d %b", "      %Y"};

  struct tm* tm_info;
  time (&timer);
  tm_info = localtime (&timer);
  strftime (buffer, 64, fmt[mode], tm_info);
...
  cairo_set_operator (g, CAIRO_OPERATOR_OVER);
  cairo_save (g);
  set_source_color(g, col, mode);
Last edited by Burunduk on Tue Jul 05, 2022 11:08 am, edited 1 time in total.
User avatar
misko_2083
Posts: 196
Joined: Wed Dec 09, 2020 11:59 pm
Has thanked: 10 times
Been thanked: 20 times

Re: Simple Clock

Post by misko_2083 »

rockedge wrote: Mon Jul 04, 2022 4:15 pm

Should be a name of a Punk Rock or Heavy metal band. "Stack Smash"

:lol:

rockedge wrote: Mon Jul 04, 2022 4:15 pm

compiled the latest version and a small problem showed up while left clicking->

Code: Select all

root# ./sclockx --color="ffffff"  --color f6aa33 --color fcfccc --color ffccff --above
*** stack smashroot# ./sclockx --color="ffffff"  --color f6aa33 --color fcfccc --color ffccff --above
*** stack smashing detected ***: terminated
Aborted

first left click changes clock format, second left click is causing stack smashing!

Very first time in all of these years I could generate stack smashing error! Didn't even know there was such a beast :o

I've compiled with -fstack-protector-all option and I can see that now.
Pushed the changes with a fix to github. https://github.com/Misko-2083/sclock
I can fix the smash but some smash protector makes select to return -1 which will exit on start very often.
Does it happens there?

masinick wrote: Mon Jul 04, 2022 4:13 pm

Wow! I'm glad that many of you got involved in writing some tools and scripts.

I found good examples long ago; the past 3-4 pages are well beyond but I was originally looking for, but as I say, if other participants want to embellish and enhance the scripts and write programs, this is precisely what "freedom" in software is all about. Congratulations to all of you; even the more "experienced" among us learned a few things along the way; I certainly learned what I was seeking, and now I have a small collection of scripts from a couple of lines to perhaps half a screen in size.

I also tried a few of the programs, though these are beyond the original scope of what I was looking for; nevertheless, this entire conversation was quite interesting and educational! Thank you all!

Well that's how it all starts not just software, from a simple solution to the over complicated one.

Burunduk wrote: Mon Jul 04, 2022 10:29 pm

On my Fossapup, strftime() produces strings up to 14 bytes long (15 with \0). The buffer in draw() is too small. char buffer[64]; solves the problem.

Thanks.
I've pushed to github.
https://github.com/Misko-2083/sclock

Last edited by misko_2083 on Tue Jul 05, 2022 11:30 am, edited 1 time in total.

Do you want to exit the Circus? The Harsh Truth
https://www.youtube.com/watch?v=ZJwQicZHp_c

User avatar
misko_2083
Posts: 196
Joined: Wed Dec 09, 2020 11:59 pm
Has thanked: 10 times
Been thanked: 20 times

Re: Simple Clock

Post by misko_2083 »

Burunduk wrote: Mon Jul 04, 2022 10:29 pm

The compiler warns that x11_fd is used uninitialized. Moved it, seems to work normally:

Code: Select all

  x11_fd = ConnectionNumber (ctx->disp);
  int nfds = x11_fd + 1;

Thanks.

Burunduk wrote: Mon Jul 04, 2022 10:29 pm

Another warning was about "comparison with string literal results in unspecified behavior". While it works because the pointers are compared, it's probably simpler to use an integer here:

Code: Select all

static void
draw (cairo_t *g, int mode,
         struct colors *col);
...
  static int mode = 0;
...
      if (ret == 0)
          draw(ctx->cr, mode, col);

      if (XPending (ctx->disp)) {
          XNextEvent (ctx->disp, &ev);
          if (XFilterEvent (&ev, ctx->w))
              continue;
          switch (ev.type) {
              case ClientMessage:
                  if (ev.xclient.data.l[0] == wm_delete_window) 
                      goto shutdown;
              case ButtonPress:
                  switch(ev.xbutton.button){
                      case Button1:
                      mode = mode == 3 ? 0 : mode + 1;
                      draw(ctx->cr, mode, col);
                      continue;
...
static void
draw (cairo_t *g, int mode,
      struct colors *col)
{
  char buffer[64];
  time_t timer;
  const char *fmt[] = {"%r", "%R:%S", "%a %d %b", "      %Y"};

  struct tm* tm_info;
  time (&timer);
  tm_info = localtime (&timer);
  strftime (buffer, 64, fmt[mode], tm_info);
...
  cairo_set_operator (g, CAIRO_OPERATOR_OVER);
  cairo_save (g);
  set_source_color(g, col, mode);

Doesn't happen on Debian Bullseye.
Try compiling with -std=c11 or -std=c99

Sorry didn't look at the code. That's a very good idea.

Maybe there is a need for an option to select a starting time format.
Like an integer between 1 & 4 or more.
Or do you think it's better to add formats as the option?
--add 1 2 3 4 where 1 2 3 4 are predefined formats
or use (--add h m s) to create a custom format (hour minute second)?

Last edited by misko_2083 on Tue Jul 05, 2022 12:24 pm, edited 1 time in total.

Do you want to exit the Circus? The Harsh Truth
https://www.youtube.com/watch?v=ZJwQicZHp_c

Burunduk
Posts: 256
Joined: Thu Jun 16, 2022 6:16 pm
Has thanked: 7 times
Been thanked: 127 times

Re: Simple Clock

Post by Burunduk »

misko_2083 wrote: Tue Jul 05, 2022 11:30 am

Doesn't happen on Debian Bullseye.
Try compiling with -std=c11 or -std=c99

It's a warning I get with -Wall. The program compiles and runs. It works because no matter how many occurrences of the identical string literals are scattered over the code, only one is stored and &"string" is always the same. This is probably not guaranteed hence the warning. Some explanations here

And in this case all those comparisons seem to be redundant, the code is shorter without.
Edit: found another link.

misko_2083 wrote:

Or do you think it's better to add formats as the option?

The custom format is probably needed for non-English locales but maybe it's better to keep things simple.
The setting for the window type can be also removed for simplicity. I don't want to remove shadows from the splash windows so still use the dnd type and no one has complained of the shadows (not every puppy has picom).

Post Reply

Return to “Scripts”