fixed issue 48

git-svn-id: http://tint2.googlecode.com/svn/trunk@62 121b4492-b84c-0410-8b4c-0d4edfb3f3cc
This commit is contained in:
lorthiois@bbsoft.fr 2009-02-25 20:04:43 +00:00
parent 9cc4916c69
commit a08622a70d
15 changed files with 228 additions and 158 deletions

View file

@ -1,17 +1,18 @@
tint2 is developped by :
- 2008 Thierry Lorthiois <lorthiois@bbsoft.fr>, project maintainer
- 2008-2009 Thierry Lorthiois <lorthiois@bbsoft.fr>, project maintainer
tint2 is based on ttm source code (http://code.google.com/p/ttm/)
- 2007 Pål Staurland <staura@gmail.com>
- 2007-2008 Pål Staurland <staura@gmail.com>
Contributors:
Kwaku Yeboah <kwakuyeboah@gmail.com> : wiki page
Daniel Moerner <dmoerner@gmail.com> : man page and debian package
Doug Barton : freebsd package
James Buren <ryuo@frugalware.org> : Frugalware package
Pierre-Emmanuel Andre <pea@raveland.org> : openbsd port
Redroar : arch package

Binary file not shown.

View file

@ -163,13 +163,14 @@ void resize_clock (void *obj)
else new_width = date_width;
new_width += (2*clock->area.paddingxlr) + (2*clock->area.pix.border.width);
if (new_width > clock->area.width || new_width < (clock->area.width-3)) {
if (new_width > clock->area.width || new_width < (clock->area.width-6)) {
int i;
Panel *panel = ((Area*)obj)->panel;
//printf("clock_width %d, new_width %d\n", clock->area.width, new_width);
printf("clock_width %d, new_width %d\n", clock->area.width, new_width);
// resize clock
clock->area.width = new_width;
// we try to limit the number of resize
clock->area.width = new_width + 1;
clock->area.posx = panel->area.width - clock->area.width - panel->area.paddingxlr - panel->area.pix.border.width;
// resize other objects on panel

View file

@ -513,7 +513,7 @@ void add_entry (char *key, char *value)
}
else
fprintf(stderr, "Invalid option: \"%s\", correct your config file\n", key);
fprintf(stderr, "tint2 : invalid option \"%s\", correct your config file\n", key);
if (value1) free (value1);
if (value2) free (value2);
@ -601,61 +601,70 @@ void config_finish ()
int config_read ()
{
const gchar * const * system_dirs;
char *path1, *path2, *dir;
char *path1;
gint i;
save_file_config = 0;
// follow XDG specification
deb:
// check tint2rc file according to XDG specification
// check tint2rc in user directory
path1 = g_build_filename (g_get_user_config_dir(), "tint2", "tint2rc", NULL);
if (!g_file_test (path1, G_FILE_TEST_EXISTS)) {
if (g_file_test (path1, G_FILE_TEST_EXISTS)) {
i = config_read_file (path1);
g_free(path1);
return i;
}
if (save_file_config) {
fprintf(stderr, "tint2 error : enable to write $HOME/.config/tint2/tint2rc\n");
exit(0);
}
// check old tintrc config file
path1 = g_build_filename (g_get_user_config_dir(), "tint", "tintrc", NULL);
if (g_file_test (path1, G_FILE_TEST_EXISTS)) {
save_file_config = 1;
old_task_font = 0;
old_time1_font = 0;
old_time2_font = 0;
config_read_file (path1);
save_config();
if (old_task_font) g_free(old_task_font);
if (old_time1_font) g_free(old_time1_font);
if (old_time2_font) g_free(old_time2_font);
goto deb;
}
else {
path2 = 0;
system_dirs = g_get_system_config_dirs();
for (i = 0; system_dirs[i]; i++) {
path2 = g_build_filename(system_dirs[i], "tint2", "tint2rc", NULL);
g_free(path1);
if (save_file_config) {
fprintf(stderr, "tint2 exit : enable to write $HOME/.config/tint2/tint2rc\n");
exit(0);
}
if (g_file_test(path2, G_FILE_TEST_EXISTS)) break;
g_free (path2);
path2 = 0;
}
// check old tintrc config file
path1 = g_build_filename (g_get_user_config_dir(), "tint", "tintrc", NULL);
if (g_file_test (path1, G_FILE_TEST_EXISTS)) {
save_file_config = 1;
old_task_font = 0;
old_time1_font = 0;
old_time2_font = 0;
config_read_file (path1);
save_config();
if (old_task_font) g_free(old_task_font);
if (old_time1_font) g_free(old_time1_font);
if (old_time2_font) g_free(old_time2_font);
g_free(path1);
goto deb;
}
if (path2) {
// copy file in user directory (path1)
dir = g_build_filename (g_get_user_config_dir(), "tint2", NULL);
if (!g_file_test (dir, G_FILE_TEST_IS_DIR)) g_mkdir(dir, 0777);
g_free(dir);
// copy tint2rc from system directory to user directory
g_free(path1);
char *path2 = 0;
system_dirs = g_get_system_config_dirs();
for (i = 0; system_dirs[i]; i++) {
path2 = g_build_filename(system_dirs[i], "tint2", "tint2rc", NULL);
copy_file(path2, path1);
g_free(path2);
}
}
}
if (g_file_test(path2, G_FILE_TEST_EXISTS)) break;
g_free (path2);
path2 = 0;
}
i = config_read_file (path1);
g_free(path1);
if (path2) {
// copy file in user directory (path1)
char *dir = g_build_filename (g_get_user_config_dir(), "tint2", NULL);
if (!g_file_test (dir, G_FILE_TEST_IS_DIR)) g_mkdir(dir, 0777);
g_free(dir);
return i;
path1 = g_build_filename (g_get_user_config_dir(), "tint2", "tint2rc", NULL);
copy_file(path2, path1);
g_free(path2);
i = config_read_file (path1);
g_free(path1);
return i;
}
return 0;
}
@ -676,7 +685,7 @@ int config_read_file (const char *path)
void save_config ()
{
fprintf(stderr, "tint2 warning : convert user's config file tintrc to tint2rc\n");
fprintf(stderr, "tint2 : convert user's config file tintrc to tint2rc\n");
char *path, *dir;
FILE *fp;

View file

@ -43,6 +43,7 @@ void server_init_atoms ()
server.atom._NET_WM_STATE_SKIP_PAGER = XInternAtom (server.dsp, "_NET_WM_STATE_SKIP_PAGER", False);
server.atom._NET_WM_STATE_SKIP_TASKBAR = XInternAtom (server.dsp, "_NET_WM_STATE_SKIP_TASKBAR", False);
server.atom._NET_WM_STATE_STICKY = XInternAtom (server.dsp, "_NET_WM_STATE_STICKY", False);
server.atom._NET_WM_STATE_DEMANDS_ATTENTION = XInternAtom (server.dsp, "_NET_WM_STATE_DEMANDS_ATTENTION", False);
server.atom._NET_WM_WINDOW_TYPE_DOCK = XInternAtom (server.dsp, "_NET_WM_WINDOW_TYPE_DOCK", False);
server.atom._NET_WM_WINDOW_TYPE_DESKTOP = XInternAtom (server.dsp, "_NET_WM_WINDOW_TYPE_DESKTOP", False);
server.atom._NET_WM_WINDOW_TYPE_TOOLBAR = XInternAtom (server.dsp, "_NET_WM_WINDOW_TYPE_TOOLBAR", False);
@ -77,6 +78,7 @@ void server_init_atoms ()
server.atom._NET_SYSTEM_TRAY_OPCODE = XInternAtom(server.dsp, "_NET_SYSTEM_TRAY_OPCODE", False);
server.atom.MANAGER = XInternAtom(server.dsp, "MANAGER", False);
server.atom._NET_SYSTEM_TRAY_MESSAGE_DATA = XInternAtom(server.dsp, "_NET_SYSTEM_TRAY_MESSAGE_DATA", False);
server.atom._NET_SYSTEM_TRAY_ORIENTATION = XInternAtom(server.dsp, "_NET_SYSTEM_TRAY_ORIENTATION", False);
}
@ -168,7 +170,7 @@ void get_root_pixmap()
server.root_pmap = ret;
if (server.root_pmap == None)
fprintf(stderr, "pixmap background detection failed\n");
fprintf(stderr, "tint2 : pixmap background detection failed\n");
else {
XGCValues gcv;
gcv.ts_x_origin = 0;
@ -272,9 +274,9 @@ next:
}
if (server.nb_desktop == 0) {
server.nb_desktop = 1;
fprintf(stderr, "tint2 warning : cannot found number of desktop.\n");
fprintf(stderr, "tint2 : cannot found number of desktop.\n");
}
fprintf(stderr, "nb monitor %d, nb desktop %d\n", server.nb_monitor, server.nb_desktop);
fprintf(stderr, "tint2 : nb monitor %d, nb desktop %d\n", server.nb_monitor, server.nb_desktop);
}

View file

@ -27,6 +27,7 @@ typedef struct Global_atom
Atom _NET_WM_STATE_SKIP_PAGER;
Atom _NET_WM_STATE_SKIP_TASKBAR;
Atom _NET_WM_STATE_STICKY;
Atom _NET_WM_STATE_DEMANDS_ATTENTION;
Atom _NET_WM_WINDOW_TYPE_DOCK;
Atom _NET_WM_WINDOW_TYPE_DESKTOP;
Atom _NET_WM_WINDOW_TYPE_TOOLBAR;
@ -57,6 +58,7 @@ typedef struct Global_atom
Atom _NET_SYSTEM_TRAY_OPCODE;
Atom MANAGER;
Atom _NET_SYSTEM_TRAY_MESSAGE_DATA;
Atom _NET_SYSTEM_TRAY_ORIENTATION;
} Global_atom;

View file

@ -36,6 +36,7 @@ GSList *icons;
#define SYSTEM_TRAY_BEGIN_MESSAGE 1
#define SYSTEM_TRAY_CANCEL_MESSAGE 2
// selection window
Window net_sel_win = None;
@ -56,7 +57,7 @@ void init_systray()
}
if (run_systray) {
if (XGetSelectionOwner(server.dsp, server.atom._NET_SYSTEM_TRAY_SCREEN) != None) {
fprintf(stderr, "tint2 warning : another systray is running\n");
fprintf(stderr, "tint2 : another systray is running\n");
run_systray = 0;
}
}
@ -64,6 +65,7 @@ void init_systray()
if (run_systray)
run_systray = net_init();
// configure sysbar on all panels
for (i=0 ; i < nb_panel ; i++) {
panel = &panel1[i];
sysbar = &panel->systray;
@ -110,6 +112,37 @@ void cleanup_systray()
}
int net_init()
{
// init systray protocol
net_sel_win = XCreateSimpleWindow(server.dsp, server.root_win, -1, -1, 1, 1, 0, 0, 0);
// v0.2 trayer specification. tint2 always orizontal.
int orient = 0;
XChangeProperty(server.dsp, net_sel_win, server.atom._NET_SYSTEM_TRAY_ORIENTATION, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &orient, 1);
XSetSelectionOwner(server.dsp, server.atom._NET_SYSTEM_TRAY_SCREEN, net_sel_win, CurrentTime);
if (XGetSelectionOwner(server.dsp, server.atom._NET_SYSTEM_TRAY_SCREEN) != net_sel_win) {
fprintf(stderr, "tint2 : can't get systray manager\n");
return 0;
}
XClientMessageEvent ev;
ev.type = ClientMessage;
ev.window = server.root_win;
ev.message_type = server.atom.MANAGER;
ev.format = 32;
ev.data.l[0] = CurrentTime;
ev.data.l[1] = server.atom._NET_SYSTEM_TRAY_SCREEN;
ev.data.l[2] = net_sel_win;
ev.data.l[3] = 0;
ev.data.l[4] = 0;
XSendEvent(server.dsp, server.root_win, False, StructureNotifyMask, &ev);
return 1;
}
int resize_systray (Systraybar *sysbar)
{
return 0;
@ -127,7 +160,7 @@ void fix_geometry()
{
GSList *it;
//* find the proper width and height
// find the proper width and height
width = 0;
height = icon_size;
for (it = icons; it != NULL; it = g_slist_next(it)) {
@ -169,109 +202,84 @@ gboolean icon_swallow(TrayWindow *traywin)
// The traywin must have its id and type set.
gboolean icon_add(Window id)
{
TrayWindow *traywin;
TrayWindow *traywin;
traywin = g_new0(TrayWindow, 1);
traywin->id = id;
traywin = g_new0(TrayWindow, 1);
traywin->id = id;
if (!icon_swallow(traywin)) {
g_free(traywin);
return FALSE;
}
if (!icon_swallow(traywin)) {
printf("not icon_swallow\n");
g_free(traywin);
return FALSE;
}
// find the positon for the systray app window
// find the positon for the systray app window
int count = g_slist_length(icons);
traywin->x = border + ((width % icon_size) / 2) +
(count % (width / icon_size)) * icon_size;
traywin->y = border + ((height % icon_size) / 2) +
(count / (height / icon_size)) * icon_size;
// add the new icon to the list
icons = g_slist_append(icons, traywin);
// add the new icon to the list
icons = g_slist_append(icons, traywin);
// watch for the icon trying to resize itself!
XSelectInput(server.dsp, traywin->id, StructureNotifyMask);
// watch for the icon trying to resize itself!
XSelectInput(server.dsp, traywin->id, StructureNotifyMask);
// position and size the icon window
XMoveResizeWindow(server.dsp, traywin->id, traywin->x, traywin->y, icon_size, icon_size);
// position and size the icon window
XMoveResizeWindow(server.dsp, traywin->id, traywin->x, traywin->y, icon_size, icon_size);
// resize our window so that the new window can fit in it
fix_geometry();
// resize our window so that the new window can fit in it
fix_geometry();
// flush before clearing, otherwise the clear isn't effective.
XFlush(server.dsp);
// make sure the new child will get the right stuff in its background
// for ParentRelative.
XClearWindow(server.dsp, win);
// flush before clearing, otherwise the clear isn't effective.
XFlush(server.dsp);
// make sure the new child will get the right stuff in its background
// for ParentRelative.
XClearWindow(server.dsp, win);
// show the window
XMapRaised(server.dsp, traywin->id);
// show the window
XMapRaised(server.dsp, traywin->id);
return TRUE;
}
int net_init()
{
// init systray protocol
net_sel_win = XCreateSimpleWindow(server.dsp, server.root_win, -1, -1, 1, 1, 0, 0, 0);
XSetSelectionOwner(server.dsp, server.atom._NET_SYSTEM_TRAY_SCREEN, net_sel_win, CurrentTime);
if (XGetSelectionOwner(server.dsp, server.atom._NET_SYSTEM_TRAY_SCREEN) != net_sel_win) {
fprintf(stderr, "tint2 warning : can't get systray manager\n");
return 0;
}
XEvent m;
m.type = ClientMessage;
m.xclient.message_type = server.atom.MANAGER;
m.xclient.format = 32;
m.xclient.data.l[0] = CurrentTime;
m.xclient.data.l[1] = server.atom._NET_SYSTEM_TRAY_SCREEN;
m.xclient.data.l[2] = net_sel_win;
m.xclient.data.l[3] = 0;
m.xclient.data.l[4] = 0;
XSendEvent(server.dsp, server.root_win, False, StructureNotifyMask, &m);
return 1;
return TRUE;
}
void net_message(XClientMessageEvent *e)
{
unsigned long opcode;
Window id;
unsigned long opcode;
Window id;
opcode = e->data.l[1];
opcode = e->data.l[1];
switch (opcode)
{
case SYSTEM_TRAY_REQUEST_DOCK: /* dock a new icon */
id = e->data.l[2];
if (id && icon_add(id))
XSelectInput(server.dsp, id, StructureNotifyMask);
break;
switch (opcode) {
case SYSTEM_TRAY_REQUEST_DOCK:
panel_refresh = 1;
id = e->data.l[2];
printf("add dockapp\n");
if (id && icon_add(id)) {
XSelectInput(server.dsp, id, StructureNotifyMask);
}
break;
case SYSTEM_TRAY_BEGIN_MESSAGE:
//g_printerr("Message From Dockapp\n");
id = e->window;
break;
case SYSTEM_TRAY_BEGIN_MESSAGE:
//g_printerr("Message From Dockapp\n");
id = e->window;
break;
case SYSTEM_TRAY_CANCEL_MESSAGE:
//g_printerr("Message Cancelled\n");
id = e->window;
break;
case SYSTEM_TRAY_CANCEL_MESSAGE:
//g_printerr("Message Cancelled\n");
id = e->window;
break;
default:
if (opcode == server.atom._NET_SYSTEM_TRAY_MESSAGE_DATA) {
//g_printerr("Text For Message From Dockapp:\n%s\n", e->data.b);
id = e->window;
break;
}
/* unknown message type. not in the spec. */
//g_printerr("Warning: Received unknown client message to System Tray selection window.\n");
break;
}
default:
if (opcode == server.atom._NET_SYSTEM_TRAY_MESSAGE_DATA) {
printf("message from dockapp:\n %s\n", e->data.b);
id = e->window;
}
// unknown message type. not in the spec
break;
}
}

View file

@ -2,12 +2,16 @@
* Copyright (C) 2009 thierry lorthiois (lorthiois@bbsoft.fr)
*
* systraybar
* based on 'docker-1.5' from Ben Jansens
*
**************************************************************************/
#ifndef SYSTRAYBAR_H
#define SYSTRAYBAR_H
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include "area.h"
@ -15,6 +19,7 @@ typedef struct {
// always start with area
Area area;
GSList *list_icons;
} Systraybar;
@ -22,17 +27,19 @@ typedef struct
{
Window id;
int x, y;
int width, height;
Window win;
long *icon_data;
int icon_width;
int icon_height;
} TrayWindow;
extern Window net_sel_win;
void init_systray();
void cleanup_systray();
int net_init();
int net_init();
void net_message(XClientMessageEvent *e);
// return 1 if task_width changed
int resize_systray (Systraybar *sysbar);

View file

@ -298,6 +298,7 @@ void draw_foreground_task (void *obj, cairo_t *c, int active)
pango_layout_set_text (layout, tsk->title, -1);
/* Drawing width and Cut text */
// pango use U+22EF or U+2026
pango_layout_set_width (layout, ((Taskbar*)tsk->area.parent)->text_width * PANGO_SCALE);
pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);

View file

@ -58,7 +58,7 @@ void init ()
server.dsp = XOpenDisplay (NULL);
if (!server.dsp) {
fprintf(stderr, "Could not open display.\n");
fprintf(stderr, "tint2 exit : could not open display.\n");
exit(0);
}
server_init_atoms ();
@ -234,10 +234,12 @@ suite:
}
void event_property_notify (Window win, Atom at)
void event_property_notify (XEvent *e)
{
int i, j;
Task *tsk;
Window win = e->xproperty.window;
Atom at = e->xproperty.atom;
if (win == server.root_win) {
if (!server.got_root_win) {
@ -245,7 +247,7 @@ void event_property_notify (Window win, Atom at)
server.got_root_win = 1;
}
/* Change number of desktops */
// Change number of desktops
else if (at == server.atom._NET_NUMBER_OF_DESKTOPS) {
server.nb_desktop = server_get_number_of_desktop ();
cleanup_taskbar();
@ -257,19 +259,19 @@ void event_property_notify (Window win, Atom at)
task_refresh_tasklist();
panel_refresh = 1;
}
/* Change desktop */
// Change desktop
else if (at == server.atom._NET_CURRENT_DESKTOP) {
server.desktop = server_get_current_desktop ();
if (panel_mode != MULTI_DESKTOP) {
visible_object();
}
}
/* Window list */
// Window list
else if (at == server.atom._NET_CLIENT_LIST) {
task_refresh_tasklist();
panel_refresh = 1;
}
/* Change active */
// Change active
else if (at == server.atom._NET_ACTIVE_WINDOW) {
GSList *l0;
if (task_active) {
@ -306,7 +308,7 @@ void event_property_notify (Window win, Atom at)
}
panel_refresh = 1;
}
/* Wallpaper changed */
// Wallpaper changed
else if (at == server.atom._XROOTPMAP_ID) {
for (i=0 ; i < nb_panel ; i++) {
set_panel_background(&panel1[i]);
@ -319,7 +321,7 @@ void event_property_notify (Window win, Atom at)
if (!tsk) return;
//printf("atom root_win = %s, %s\n", XGetAtomName(server.dsp, at), tsk->title);
/* Window title changed */
// Window title changed
if (at == server.atom._NET_WM_VISIBLE_NAME || at == server.atom._NET_WM_NAME || at == server.atom.WM_NAME) {
Task *tsk2;
GSList *l0;
@ -338,9 +340,18 @@ void event_property_notify (Window win, Atom at)
}
panel_refresh = 1;
}
/* Iconic state */
// Demand attention
else if (at == server.atom._NET_WM_STATE) {
if (window_is_urgent (win)) {
printf(" event_property_notify _NET_WM_STATE_DEMANDS_ATTENTION\n");
}
else {
}
}
else if (at == server.atom.WM_STATE) {
if (window_is_iconified (win))
// Iconic state
// TODO : try to delete following code
if (window_is_iconified (win)) {
if (task_active) {
if (task_active->win == tsk->win) {
Task *tsk2;
@ -356,8 +367,9 @@ void event_property_notify (Window win, Atom at)
task_active = 0;
}
}
}
/* Window icon changed */
}
}
// Window icon changed
else if (at == server.atom._NET_WM_ICON) {
get_icon(tsk);
Task *tsk2;
@ -377,7 +389,7 @@ void event_property_notify (Window win, Atom at)
}
panel_refresh = 1;
}
/* Window desktop changed */
// Window desktop changed
else if (at == server.atom._NET_WM_DESKTOP) {
remove_task (tsk);
add_task (win);
@ -493,7 +505,7 @@ load_config:
break;
case PropertyNotify:
event_property_notify (e.xproperty.window, e.xproperty.atom);
event_property_notify (&e);
break;
case ConfigureNotify:
@ -505,6 +517,7 @@ load_config:
case UnmapNotify:
case DestroyNotify:
// printf("destroy client\n");
/*
GSList *it;
for (it = icons; it; it = g_slist_next(it)) {
@ -516,10 +529,10 @@ load_config:
break;
case ClientMessage:
break;
if (e.xclient.message_type == server.atom._NET_SYSTEM_TRAY_OPCODE && e.xclient.format == 32)
// && e.xclient.window == net_sel_win)
//printf("ClientMessage\n");
if (e.xclient.message_type == server.atom._NET_SYSTEM_TRAY_OPCODE && e.xclient.format == 32 && e.xclient.window == net_sel_win) {
net_message(&e.xclient);
}
break;
}
}

BIN
src/tint2

Binary file not shown.

View file

@ -140,6 +140,23 @@ int window_is_iconified (Window win)
}
int window_is_urgent (Window win)
{
Atom *at;
int count, i;
at = server_get_property (win, server.atom._NET_WM_STATE, XA_ATOM, &count);
for (i = 0; i < count; i++) {
if (at[i] == server.atom._NET_WM_STATE_DEMANDS_ATTENTION) {
XFree(at);
return 1;
}
}
XFree(at);
return 0;
}
int server_get_number_of_desktop ()
{
return get_property32(server.root_win, server.atom._NET_NUMBER_OF_DESKTOPS, XA_CARDINAL);

View file

@ -18,6 +18,7 @@ void set_close (Window win);
int server_get_current_desktop ();
int server_get_number_of_desktop ();
int window_is_iconified (Window win);
int window_is_urgent (Window win);
int window_is_hidden (Window win);
int window_is_active (Window win);
int get_icon_count (long *data, int num);

View file

@ -25,7 +25,7 @@ border_color = #ffffff 70
#---------------------------------------------
panel_monitor = all
panel_position = bottom center
panel_size = 88% 28
panel_size = 90% 28
panel_margin = 0 0
panel_padding = 7 0
font_shadow = 0

View file

@ -27,13 +27,15 @@ panel_monitor = all
panel_position = bottom center
panel_size = 92% 30
panel_margin = 0 0
panel_padding = 7 3
panel_padding = 7 3 7
font_shadow = 0
panel_background_id = 1
#---------------------------------------------
# TASKBAR
#---------------------------------------------
#taskbar_mode = single_desktop
#taskbar_mode = single_monitor
taskbar_mode = multi_desktop
taskbar_padding = 0 0 0
taskbar_background_id = 2
@ -52,6 +54,12 @@ task_active_font_color = #ffffff 100
task_background_id = 0
task_active_background_id = 3
#---------------------------------------------
# SYSTRAYBAR
#---------------------------------------------
systray_padding = 4 2 2
systray_background_id = 2
#---------------------------------------------
# CLOCK
#---------------------------------------------