make neko have somewhat of a sleep schedule

This commit is contained in:
Virt 2025-03-09 00:11:06 +01:00
commit 0917c3f9aa
3 changed files with 148 additions and 111 deletions

207
wayneko.c
View file

@ -32,11 +32,10 @@ const char usage[] =
" --background-colour 0xRRGGBB[AA]\n"
" --outline-colour 0xRRGGBB[AA]\n"
" --type neko|inu|random\n"
" --idle-sleep num\n"
" --sleepiness num\n"
" --sleepiness-night num\n"
" --idle-sleep num\n"
" --phase-sleep num-num\n"
" --phase-awake num-num\n"
" --layer background|bottom|top|overlay\n"
" --follow-pointer true|false\n"
" --survive-close\n"
"\n";
@ -73,19 +72,31 @@ enum Neko
NEKO_RUN_LEFT_2,
};
const uint16_t animation_timeout = 200;
const uint16_t animation_timeout = 200; /* milliseconds */
size_t animation_ticks_until_next_frame = 10;
enum Neko current_neko = NEKO_STARE;
enum Type type = NEKO;
bool follow_pointer = true;
bool follow_pointer = false; // TODO: implement this again
bool recreate_surface_on_close = false;
enum zwlr_layer_shell_v1_layer layer = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM;
struct ext_idle_notifier_v1 *idle_notifier = NULL;
uint32_t neko_idle_timeout_ms = 180000; /* 3 minutes. */
int sleepiness = 4;
int sleepiness_night = 5;
enum Phase {
PHASE_AWAKE,
PHASE_SLEEP
};
typedef enum Phase Phase;
/* wayneko will wake up at startup */
Phase current_phase = PHASE_SLEEP;
size_t ticks_until_phase_change = 0;
uint32_t phase_sleep_min = 450;
uint32_t phase_sleep_max = 1800;
uint32_t phase_awake_min = 120;
uint32_t phase_awake_max = 600;
struct Seat
{
@ -167,15 +178,15 @@ static void handle_error (int signum)
{
const char *msg =
"\n"
"🐈 🐈 🐈\n"
"┌──────────────────────────────────────────┐\n"
"│ │\n"
"│ wayneko has crashed. \n"
"│ wayneko has crashed :(\n"
"│ │\n"
"│ This is likely a bug, so please │\n"
"│ report this to the mailing list. │\n"
"│ This is likely a bug in the fork │\n"
"│ you are running, so feel free to │\n"
"│ report your stuff there. │\n"
"│ │\n"
"~leon_plickat/public-inbox@lists.sr.ht\n"
" https://copeberg.org/virt/wayneko \n"
"│ │\n"
"└──────────────────────────────────────────┘\n"
"\n";
@ -823,11 +834,8 @@ static bool animtation_neko_wants_sleep (void)
}
/** Returns true if new frame is needed. */
static bool animation_next_state_with_idle (void)
{
/* If no one is there (system is idle), neko gets bored and will sleep. */
switch (current_neko)
{
static bool animation_next_state_sleep(void) {
switch (current_neko) {
case NEKO_SLEEP_1:
case NEKO_SLEEP_2:
case NEKO_YAWN:
@ -919,39 +927,9 @@ static bool animation_next_state_with_hotspot (uint32_t x)
}
/** Returns true if new frame is needed. */
static bool animation_next_state_normal (void)
static bool animation_next_state_awake (void)
{
/* Sleep at night, but with a small chance to wake up and do something.
* If the neko is already awake, slightly higher chance to stay awake.
*/
const bool neko_is_sleeping = current_neko == NEKO_SLEEP_1 || current_neko == NEKO_SLEEP_2;
if ( animtation_neko_wants_sleep() && (( neko_is_sleeping && rand() % sleepiness_night != 0 ) || ( !neko_is_sleeping && rand() % 2 != 0 )) )
{
switch (current_neko)
{
case NEKO_RUN_RIGHT_1:
case NEKO_RUN_RIGHT_2:
case NEKO_RUN_LEFT_1:
case NEKO_RUN_LEFT_2:
animation_neko_do_stare(true);
return true;
case NEKO_SLEEP_1:
case NEKO_SLEEP_2:
animation_neko_do_sleep();
return true;
default:
if (rand() % 3 == 0)
animation_neko_do_yawn();
else
animation_neko_do_stare(false);
return true;
}
}
switch (current_neko)
{
switch (current_neko) {
case NEKO_STARE:
switch (rand() % 24)
{
@ -960,9 +938,6 @@ static bool animation_next_state_normal (void)
break;
case 1:
animation_neko_do_sleep();
break;
case 2:
animation_neko_do_yawn();
break;
@ -1014,15 +989,10 @@ static bool animation_next_state_normal (void)
case NEKO_SLEEP_1:
case NEKO_SLEEP_2:
if ( rand() % sleepiness == 0 )
{
if ( rand() % 2 == 0 )
animation_neko_do_shock();
else
animation_neko_do_stare(false);
}
if ( rand() % 2 == 0 )
animation_neko_do_shock();
else
animation_neko_do_sleep();
animation_neko_do_yawn();
return true;
case NEKO_SCRATCH_1:
@ -1041,12 +1011,6 @@ static bool animation_next_state_normal (void)
return true;
case NEKO_YAWN:
if ( rand() % 2 == 0 )
animation_neko_do_stare(false);
else
animation_neko_do_sleep();
return true;
case NEKO_SHOCK:
animation_neko_do_stare(true);
return true;
@ -1056,27 +1020,61 @@ static bool animation_next_state_normal (void)
return false;
}
/** called once per tick, checks for a phase change, and changes the phase if required */
static void check_phase(void) {
if (ticks_until_phase_change > 0)
ticks_until_phase_change--;
else {
// change phase
uint32_t min = 0, max = 0;
if (current_phase == PHASE_AWAKE) {
current_phase = PHASE_SLEEP;
min = phase_sleep_min;
max = phase_sleep_max;
} else {
current_phase = PHASE_AWAKE;
min = phase_awake_min;
max = phase_awake_max;
}
uint32_t phase_change_seconds = min + (((uint32_t) rand()) % (max - min));
ticks_until_phase_change = phase_change_seconds * 1000 / animation_timeout;
}
}
/** Returns true if new frame is needed. */
static bool animation_next_state (void)
{
check_phase();
if ( animation_ticks_until_next_frame > 0 )
{
animation_ticks_until_next_frame--;
return false;
}
bool idle = false;
struct Seat *seat;
wl_list_for_each(seat, &seats, link)
{
if (seat->currently_idle)
return animation_next_state_with_idle();
wl_list_for_each(seat, &seats, link) {
if (seat->currently_idle) idle = true;
}
wl_list_for_each(seat, &seats, link)
{
if (seat->on_surface)
return animation_next_state_with_hotspot(seat->surface_x);
if (false) {
wl_list_for_each(seat, &seats, link) {
if (seat->on_surface)
return animation_next_state_with_hotspot(seat->surface_x);
}
}
return animation_next_state_normal();
Phase phase = current_phase;
if (idle) phase = PHASE_SLEEP;
if (phase == PHASE_SLEEP)
return animation_next_state_sleep();
else
return animation_next_state_awake();
}
/*************
@ -1397,6 +1395,9 @@ int main (int argc, char *argv[])
}
else if ( strcmp(argv[i], "--follow-pointer") == 0 )
{
fprintf(stderr, "ERROR: Functionality for flag '--follow-pointer' is not yet implemented in this fork.\n");
return EXIT_FAILURE;
const char *t = get_argument(argc, argv, &i);
if ( t == NULL )
return EXIT_FAILURE;
@ -1435,27 +1436,49 @@ int main (int argc, char *argv[])
return EXIT_FAILURE;
}
}
else if ( strcmp(argv[i], "--sleepiness") == 0 )
else if ( strcmp(argv[i], "--awake-phase") == 0 )
{
const char *a = get_argument(argc, argv, &i);
int i = atoi(a);
if (i != 0)
sleepiness = abs(i) + 1;
else
{
fprintf(stderr, "ERROR: Invalid argument '%s' for flag '--sleepiness'.\n", a);
char* a = get_argument(argc, argv, &i);
char* b = a;
while (*b != '-' && *b != 0) b++;
if (*b == 0) {
fprintf(stderr, "ERROR: Values in '%s' for flag '--awake-phase' are must be split by a dash.\n", a);
return EXIT_FAILURE;
}
*b++ = 0;
int min = atoi(a);
int max = atoi(b);
if (min != 0 && max != 0 && max != min) {
phase_awake_min = (uint32_t) abs(min);
phase_awake_max = (uint32_t) abs(max);
} else {
fprintf(stderr, "ERROR: Invalid numbers in argument '%s-%s' for flag '--awake-phase'.\n", a, b);
return EXIT_FAILURE;
}
}
else if ( strcmp(argv[i], "--sleepiness-night") == 0 )
else if ( strcmp(argv[i], "--sleep-phase") == 0 )
{
const char *a = get_argument(argc, argv, &i);
int i = atoi(a);
if (i != 0)
sleepiness_night = abs(i) + 1;
else
{
fprintf(stderr, "ERROR: Invalid argument '%s' for flag '--sleepiness-night'.\n", a);
char* a = get_argument(argc, argv, &i);
char* b = a;
while (*b != '-' && *b != 0) b++;
if (*b == 0) {
fprintf(stderr, "ERROR: Values in '%s' for flag '--sleep-phase' are must be split by a dash.\n", a);
return EXIT_FAILURE;
}
*b++ = 0;
int min = atoi(a);
int max = atoi(b);
if (min != 0 && max != 0 && max != min) {
phase_sleep_min = (uint32_t) abs(min);
phase_sleep_max = (uint32_t) abs(max);
} else {
fprintf(stderr, "ERROR: Invalid numbers in argument '%s-%s' for flag '--sleep-phase'.\n", a, b);
return EXIT_FAILURE;
}
}