mirror of https://github.com/fairyglade/ly
Commit unfinished full rewrite (Zig 0.11.0)
What needs to be dealt with: - Matrix animation - Authentication part - Testing on actual TTY (not just virtual console) Signed-off-by: AnErrupTion <anerruption@disroot.org>pull/597/head
parent
d1cf1ebd80
commit
92e1f083a1
@ -1,10 +1,14 @@
|
||||
.{
|
||||
.name = "ly",
|
||||
.version = "0.7.0",
|
||||
.version = "1.0.0",
|
||||
.dependencies = .{
|
||||
.ini = .{
|
||||
.url = "https://github.com/AnErrupTion/zig-ini/archive/a40f3cae04939de0d795812a73633e0563cf3132.tar.gz",
|
||||
.hash = "1220b39fc35714d235fd9633422f5ba3c4aba44f1ecaf29cf91515963f0b08a3da89",
|
||||
.url = "https://github.com/AnErrupTion/zig-ini/archive/7ab77196a4dc63d1ede50e0a1af1a8325c152f2f.tar.gz",
|
||||
.hash = "12204b1d133060dd0c4304d5e0fbdb33a70038118e7c112b14b5f8d176ea15cd5939",
|
||||
},
|
||||
.clap = .{
|
||||
.url = "https://github.com/Hejsil/zig-clap/archive/f49b94700e0761b7514abdca0e4f0e7f3f938a93.tar.gz",
|
||||
.hash = "1220f48518ce22882e102255ed3bcdb7aeeb4891f50b2cdd3bd74b5b2e24d3149ba2",
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +0,0 @@
|
||||
bin
|
||||
obj
|
@ -1,13 +0,0 @@
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
Version 2, December 2004
|
||||
|
||||
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim or modified
|
||||
copies of this license document, and changing it is allowed as long
|
||||
as the name is changed.
|
||||
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
@ -1,95 +0,0 @@
|
||||
# Configator
|
||||
Configator is a lightweight library for ini config file parsing.
|
||||
This was created to make it easy following the "DRY" coding rule
|
||||
without using macros, and with flexibility in mind.
|
||||
|
||||
It integrates very well with the [Argoat](https://github.com/nullgemm/argoat.git)
|
||||
arguments parser library, by using the same function pointers format.
|
||||
This way, you can easily load settings from an ini file while overloading them
|
||||
with command-line arguments if needed: the handling functions will be the same.
|
||||
|
||||
Configator does not use any macro or dynamic memory allocation,
|
||||
and was built in less than 350 lines of C99 code.
|
||||
|
||||
## Testing
|
||||
Run `make` to compile an example executable and perform basic testing
|
||||
|
||||
## Using
|
||||
### TL;DR
|
||||
Please see `example.c` for the condensed version
|
||||
(or better, read the actual documentation below).
|
||||
It is a bit too long to be copied here twice...
|
||||
|
||||
### Details
|
||||
Include `argoat.h` and compile `argoat.c` with your code.
|
||||
|
||||
Write the functions that will handle your parameters.
|
||||
They will be called during the parsing process, in the order given by the user
|
||||
```
|
||||
void handle_config_u8(void* data, char** value, const int pars_count)
|
||||
{
|
||||
if (pars_count > 0)
|
||||
{
|
||||
*((uint8_t*) data) = atoi(*value);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In your `main`, declare the variables to configure.
|
||||
They will be passed to the corresponding functions as `void* data`
|
||||
```
|
||||
uint8_t answer = 0;
|
||||
```
|
||||
|
||||
Declare the arrays of parameters by section, starting with the general section.
|
||||
If you don't want to handle parameters in some section, just declare it `NULL`.
|
||||
```
|
||||
struct configator_param* map_no_section = NULL;
|
||||
```
|
||||
|
||||
Declare real sections parameters afterwards
|
||||
```
|
||||
struct configator_param map_test_section[] =
|
||||
{
|
||||
{"ping", &answer, handle_config_u8},
|
||||
{"pong", &answer, handle_config_u8},
|
||||
};
|
||||
```
|
||||
|
||||
Then group them in the map
|
||||
```
|
||||
struct configator_param* map[] =
|
||||
{
|
||||
map_no_section,
|
||||
map_test_section
|
||||
};
|
||||
```
|
||||
|
||||
And declare the sections array. Configator will execute the pointed function
|
||||
with `NULL` arguments at the beginning of each detected section.
|
||||
You can also declare sections with `NULL` parameters, in which case nothing
|
||||
will be executed.
|
||||
```
|
||||
struct configator_param sections[] =
|
||||
{
|
||||
{"network_test", &answer, handle_config_u8},
|
||||
};
|
||||
```
|
||||
|
||||
Don't forget to put the right numbers in the lenght variables
|
||||
```
|
||||
uint16_t map_len[] = {0, 2};
|
||||
uint16_t sections_len = 1;
|
||||
```
|
||||
|
||||
Then initialize and use configator
|
||||
```
|
||||
struct configator config;
|
||||
config.map = map;
|
||||
config.map_len = map_len;
|
||||
config.sections = sections;
|
||||
config.sections_len = sections_len;
|
||||
|
||||
configator(&config, "config.ini");
|
||||
printf("answer = %d\n", answer);
|
||||
```
|
@ -1,37 +0,0 @@
|
||||
sectionless = 0000
|
||||
|
||||
[sections_test]
|
||||
[regular]
|
||||
[left-padded]
|
||||
[right-padded]
|
||||
[middle-padded]
|
||||
[smaller]
|
||||
[ right]
|
||||
[left ]
|
||||
[ middle ]
|
||||
[]
|
||||
[ ]
|
||||
[three wrong words]
|
||||
[]]
|
||||
[[]
|
||||
[[]]
|
||||
|
||||
[params_tests]
|
||||
# comment
|
||||
# padded comment
|
||||
test = 1111
|
||||
left-padded = 2222
|
||||
right-padded = 3333
|
||||
minified=4444
|
||||
multi = 4444 3333 2222 1111
|
||||
spaces = " middle "
|
||||
wrong declaration = 5555
|
||||
wrong line
|
||||
|
||||
[test_section]
|
||||
answer = 42
|
||||
|
||||
[network_test]
|
||||
ping = 255
|
||||
|
||||
[question]
|
@ -1,309 +0,0 @@
|
||||
#include "configator.h"
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// returns the index of the searched element, or len if it can't be found
|
||||
static uint16_t search(struct configator_param* config, uint16_t len, char* key)
|
||||
{
|
||||
// strcmp indicator
|
||||
int8_t disc;
|
||||
// initial tested index
|
||||
uint16_t i = len / 2;
|
||||
// initial bounds (inclusive)
|
||||
uint16_t k = 0;
|
||||
uint16_t l = len - 1;
|
||||
|
||||
// skip directly to the final check
|
||||
if (len > 1)
|
||||
{
|
||||
// as long as a match is possible
|
||||
do
|
||||
{
|
||||
disc = strcmp(config[i].key, key);
|
||||
|
||||
if (disc == 0)
|
||||
{
|
||||
// found by chance
|
||||
return i;
|
||||
}
|
||||
else if (disc > 0)
|
||||
{
|
||||
l = i;
|
||||
i = (i + k) / 2; // floor
|
||||
}
|
||||
else
|
||||
{
|
||||
k = i;
|
||||
i = (i + l) / 2 + (i + l) % 2; // ceil
|
||||
}
|
||||
|
||||
if (len == 2)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
while ((k+1) != l);
|
||||
}
|
||||
|
||||
if (len > 0)
|
||||
{
|
||||
// final check
|
||||
disc = strcmp(config[i].key, key);
|
||||
|
||||
if (disc == 0)
|
||||
{
|
||||
// found by dichotomy
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
// not found
|
||||
return len;
|
||||
}
|
||||
|
||||
static void configator_save_section(struct configator* config, char* line)
|
||||
{
|
||||
char c;
|
||||
uint16_t index;
|
||||
uint16_t k = 0; // last non-space pos
|
||||
uint16_t l = 0; // second last non-space pos
|
||||
|
||||
// leading spaces
|
||||
do
|
||||
{
|
||||
++line;
|
||||
c = line[0];
|
||||
}
|
||||
while ((c != '\0') && isspace(c));
|
||||
|
||||
if (c == '[')
|
||||
{
|
||||
++line;
|
||||
c = line[0];
|
||||
}
|
||||
|
||||
// trailing spaces
|
||||
for (uint16_t i = 1; c != '\0'; ++i)
|
||||
{
|
||||
if ((c != ']') && !isspace(c))
|
||||
{
|
||||
// we use two variables to avoid
|
||||
// counting the ending ']'
|
||||
l = k + 1; // we *must* increment here
|
||||
k = i;
|
||||
}
|
||||
|
||||
c = line[i];
|
||||
}
|
||||
|
||||
// terminator
|
||||
line[l] = '\0';
|
||||
|
||||
if (l == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// saving
|
||||
strncpy(config->section, line, l + 1);
|
||||
|
||||
// searching
|
||||
index = search(
|
||||
config->sections,
|
||||
config->sections_len,
|
||||
config->section);
|
||||
|
||||
#ifdef CONFIGATOR_DEBUG
|
||||
printf("[%s]\n", line);
|
||||
#endif
|
||||
|
||||
// calling the function
|
||||
if (index != config->sections_len)
|
||||
{
|
||||
config->current_section = index + 1;
|
||||
|
||||
if (config->sections[index].handle != NULL)
|
||||
{
|
||||
config->sections[index].handle(
|
||||
config->sections[index].data,
|
||||
NULL,
|
||||
0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void configator_save_param(struct configator* config, char* line)
|
||||
{
|
||||
char c;
|
||||
uint16_t index;
|
||||
uint16_t i = 0;
|
||||
uint16_t k = 0;
|
||||
|
||||
// leading chars
|
||||
do
|
||||
{
|
||||
++i;
|
||||
c = line[i];
|
||||
}
|
||||
while ((c != '\0') && (c != '=') && !isspace(c));
|
||||
|
||||
// empty line
|
||||
if (c == '\0')
|
||||
{
|
||||
config->param[0] = '\0';
|
||||
config->value[0] = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
// end of the param
|
||||
k = i;
|
||||
|
||||
// spaces before next char if any
|
||||
while ((c != '\0') && isspace(c))
|
||||
{
|
||||
++i;
|
||||
c = line[i];
|
||||
}
|
||||
|
||||
// that next char must be '='
|
||||
if (c != '=')
|
||||
{
|
||||
config->param[0] = '\0';
|
||||
config->value[0] = '\0';
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
c = line[i];
|
||||
}
|
||||
|
||||
// spaces after '='
|
||||
while ((c != '\0') && isspace(c))
|
||||
{
|
||||
++i;
|
||||
c = line[i];
|
||||
}
|
||||
|
||||
line[k] = '\0';
|
||||
strncpy(config->param, line, k + 1);
|
||||
strncpy(config->value, line + i, strlen(line + i) + 1);
|
||||
|
||||
// searching
|
||||
if ((config->current_section == 0) && (config->map_len[0] == 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
index = search(
|
||||
config->map[config->current_section],
|
||||
config->map_len[config->current_section],
|
||||
config->param);
|
||||
|
||||
#ifdef CONFIGATOR_DEBUG
|
||||
printf("%s = \"%s\"\n", config->param, config->value);
|
||||
#endif
|
||||
|
||||
// calling the function
|
||||
if ((index != config->map_len[config->current_section])
|
||||
&& (config->map[config->current_section][index].handle != NULL))
|
||||
{
|
||||
char* tmp = (char*) config->value;
|
||||
config->map[config->current_section][index].handle(
|
||||
config->map[config->current_section][index].data,
|
||||
&(tmp),
|
||||
1);
|
||||
}
|
||||
}
|
||||
|
||||
static void configator_read(FILE* fp, char* line)
|
||||
{
|
||||
int c = fgetc(fp);
|
||||
uint16_t i = 0;
|
||||
uint16_t k = 0;
|
||||
|
||||
if (c == EOF)
|
||||
{
|
||||
line[0] = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
while ((c != '\n') && (c != EOF))
|
||||
{
|
||||
if ((i < (CONFIGATOR_MAX_LINE + 1)) // maximum len
|
||||
&& ((i > 0) || !isspace(c))) // skips leading spaces
|
||||
{
|
||||
// used to trim trailing spaces
|
||||
// and to terminate overflowing string
|
||||
if (!isspace(c))
|
||||
{
|
||||
k = i;
|
||||
}
|
||||
|
||||
line[i] = c;
|
||||
++i;
|
||||
}
|
||||
|
||||
c = fgetc(fp);
|
||||
}
|
||||
|
||||
if (i == (CONFIGATOR_MAX_LINE + 1))
|
||||
{
|
||||
line[k] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
line[k + 1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
int configator(struct configator* config, const char* path)
|
||||
{
|
||||
FILE* fp = fopen(path, "r");
|
||||
|
||||
if (fp == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
config->section[0] = '\0';
|
||||
config->param[0] = '\0';
|
||||
config->value[0] = '\0';
|
||||
config->current_section = 0;
|
||||
|
||||
// event loop
|
||||
char line[CONFIGATOR_MAX_LINE + 1];
|
||||
|
||||
while (1)
|
||||
{
|
||||
configator_read(fp, line);
|
||||
|
||||
// end of file
|
||||
if (feof(fp))
|
||||
{
|
||||
break;
|
||||
}
|
||||
// comment
|
||||
else if (line[0] == '#')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// section
|
||||
else if ((line[0] == '[') && (line[strlen(line) - 1] == ']'))
|
||||
{
|
||||
configator_save_section(config, line);
|
||||
}
|
||||
// param
|
||||
else
|
||||
{
|
||||
configator_save_param(config, line);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
#ifndef H_CONFIGATOR
|
||||
#define H_CONFIGATOR
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define CONFIGATOR_MAX_LINE 80
|
||||
|
||||
#if 0
|
||||
#define CONFIGATOR_DEBUG
|
||||
#endif
|
||||
|
||||
struct configator_param
|
||||
{
|
||||
char* key;
|
||||
void* data;
|
||||
void (*handle)(void* data, char** value, const int pars_count);
|
||||
};
|
||||
|
||||
struct configator
|
||||
{
|
||||
char section[CONFIGATOR_MAX_LINE];
|
||||
char param[CONFIGATOR_MAX_LINE];
|
||||
char value[CONFIGATOR_MAX_LINE];
|
||||
uint16_t current_section;
|
||||
|
||||
struct configator_param** map;
|
||||
struct configator_param* sections;
|
||||
|
||||
uint16_t* map_len;
|
||||
uint16_t sections_len;
|
||||
};
|
||||
|
||||
int configator(struct configator* config, const char* path);
|
||||
|
||||
#endif
|
@ -1,71 +0,0 @@
|
||||
#include "configator.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
void handle_config_u8(void* data, char** value, const int pars_count)
|
||||
{
|
||||
if (pars_count > 0)
|
||||
{
|
||||
*((uint8_t*) data) = atoi(*value);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_question(void* data, char** value, const int pars_count)
|
||||
{
|
||||
*((uint8_t*) data) = 23;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
uint8_t answer = 0;
|
||||
uint8_t question = 0;
|
||||
|
||||
// parameters, grouped in sections
|
||||
struct configator_param* map_no_section = NULL;
|
||||
struct configator_param* map_question_section = NULL;
|
||||
struct configator_param map_test_section[] =
|
||||
{
|
||||
{"aaabbb", &answer, handle_config_u8},
|
||||
{"aabbaa", &answer, handle_config_u8},
|
||||
{"answer", &answer, handle_config_u8},
|
||||
{"cccccc", &answer, handle_config_u8},
|
||||
{"cccddd", &answer, handle_config_u8},
|
||||
{"daaaaa", &answer, handle_config_u8},
|
||||
{"ddaaaa", &answer, handle_config_u8},
|
||||
{"eeeeee", &answer, handle_config_u8}
|
||||
};
|
||||
struct configator_param* map[] =
|
||||
{
|
||||
map_no_section,
|
||||
map_question_section,
|
||||
map_test_section
|
||||
};
|
||||
|
||||
// sections (used to execute functions at sections start)
|
||||
struct configator_param sections[] =
|
||||
{
|
||||
{"question", &question, handle_question},
|
||||
{"test_section", NULL, NULL},
|
||||
};
|
||||
|
||||
// number of parameters, by section
|
||||
uint16_t map_len[] = {0, 0, 8};
|
||||
// number of sections
|
||||
uint16_t sections_len = 2;
|
||||
|
||||
// configator object
|
||||
struct configator config;
|
||||
config.map = map;
|
||||
config.map_len = map_len;
|
||||
config.sections = sections;
|
||||
config.sections_len = sections_len;
|
||||
|
||||
// execute configuration
|
||||
configator(&config, "config.ini");
|
||||
printf("question = %d\n", question);
|
||||
printf("answer = %d\n", answer);
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
#ifndef H_DRAGONFAIL_ERROR
|
||||
#define H_DRAGONFAIL_ERROR
|
||||
|
||||
enum dgn_error
|
||||
{
|
||||
DGN_OK, // do not remove
|
||||
|
||||
DGN_NULL,
|
||||
DGN_ALLOC,
|
||||
DGN_BOUNDS,
|
||||
DGN_DOMAIN,
|
||||
|
||||
DGN_SIZE, // do not remove
|
||||
};
|
||||
|
||||
//#define DRAGONFAIL_SKIP
|
||||
#define DRAGONFAIL_BASIC_LOG
|
||||
#define DRAGONFAIL_THROW_BASIC_LOG
|
||||
#define DRAGONFAIL_THROW_DEBUG_LOG
|
||||
//#define DRAGONFAIL_ABORT
|
||||
|
||||
#endif
|
@ -1,46 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include "dragonfail.h"
|
||||
|
||||
int div(int num, int den)
|
||||
{
|
||||
if (den == 0)
|
||||
{
|
||||
dgn_throw(DGN_DOMAIN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return num / den;
|
||||
}
|
||||
|
||||
void log_init(char** log)
|
||||
{
|
||||
log[DGN_OK] = "out-of-bounds log message"; // special case
|
||||
log[DGN_NULL] = "null pointer";
|
||||
log[DGN_ALLOC] = "failed memory allocation";
|
||||
log[DGN_BOUNDS] = "out-of-bounds index";
|
||||
log[DGN_DOMAIN] = "invalid domain";
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
log_init(dgn_init());
|
||||
|
||||
int i;
|
||||
int q;
|
||||
|
||||
for (i = -2; i < 3; ++i)
|
||||
{
|
||||
q = div(42, i);
|
||||
|
||||
if (dgn_catch())
|
||||
{
|
||||
printf("skipping division by zero\n");
|
||||
dgn_reset();
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("42/%d = %d\n", i, q);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
Version 2, December 2004
|
||||
|
||||
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim or modified
|
||||
copies of this license document, and changing it is allowed as long
|
||||
as the name is changed.
|
||||
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
@ -1,77 +0,0 @@
|
||||
# Dragonfail
|
||||
Dragonfail is a simple library providing basic error handling functionnalities.
|
||||
It was designed to be as lightweight as possible, and can be completely disabled
|
||||
with only one `#define` (more on that later).
|
||||
|
||||
Dragonfail was designed to be fast and uses inline functions exclusively. These
|
||||
calls modify a global context which is not directly accessible by the programmer.
|
||||
|
||||
All the error codes must be written in an enum in **your** `dragonfail_error.h` file.
|
||||
Because of this rather unusual architecture, the file must be found by the compiler
|
||||
when it is processing `dragonfail.c` (in addition to your own source code of course).
|
||||
|
||||
## Testing
|
||||
Run `make` to compile an example, and `make run` to execute it.
|
||||
|
||||
## Defines
|
||||
This header can also contain some `#define` to modify dragonfail's behaviour:
|
||||
- `DRAGONFAIL_SKIP` completely disables the whole library, making it completely
|
||||
disappear from the binary (unless your compiler is a massive douche).
|
||||
- `DRAGONFAIL_BASIC_LOG` enables the `dgn_basic_log()` function calls
|
||||
- `DRAGONFAIL_THROW_BASIC_LOG` makes `dgn_throw()` call `dgn_basic_log()` automatically
|
||||
- `DRAGONFAIL_THROW_DEBUG_LOG` also prints the file and line in which
|
||||
`dgn_throw()` is called (you don't even need to compile with symbols
|
||||
because this is achieved the smart way using simple C99 macros)
|
||||
- `DRAGONFAIL_ABORT` makes `dgn_throw()` call `abort()`
|
||||
|
||||
Again, these `#define` must be placed in **your** `dragonfail_error.h` file.
|
||||
|
||||
## Using
|
||||
### TL;DR
|
||||
see the `example` folder :)
|
||||
|
||||
### Documentation
|
||||
```
|
||||
char** dgn_init();
|
||||
```
|
||||
This intializes the context to `DGN_OK` (no error) and returns the array of strings
|
||||
you can fill with log messages corresponding to the errors you added in the enum.
|
||||
|
||||
```
|
||||
void dgn_reset();
|
||||
```
|
||||
This resets the context to `DGN_OK`.
|
||||
|
||||
```
|
||||
void dgn_basic_log();
|
||||
```
|
||||
This prints the message corresponding to the current error to stderr.
|
||||
|
||||
```
|
||||
void dgn_throw(enum dgn_error new_code);
|
||||
```
|
||||
This sets the error to the given code.
|
||||
|
||||
```
|
||||
char dgn_catch();
|
||||
```
|
||||
This returns true if the context currently holds an error
|
||||
|
||||
## Why is the architecture so strange?
|
||||
The dragonfail context is global (extern) but really *implemented* in `dragonfail.c`.
|
||||
Its type depends on the size of the enum so it is *declared* in `dragonfail_private.h`:
|
||||
this way we can include the user's `dragonfail_error.h` and get `DGN_SIZE`.
|
||||
|
||||
The inline functions need to access this context and **we want it private**, so we can't
|
||||
*implement* them directly in the header as a lot of people seem to appreciate. Instead,
|
||||
we will *declare* them here, and put the *implementations* in `dragonfail.c`: this way
|
||||
we can access the global context without including its declaration, because it is
|
||||
implemented here as well.
|
||||
|
||||
When you include `dragonfail.h`, you get access to the inline functions declarations
|
||||
and thanks to this design any compiler will do the rest of the job automatically. Yes,
|
||||
this whole thing is useless and over-engineered. And yes, I had fun doing it...
|
||||
|
||||
## Greetings
|
||||
Jinjer for the cool music \m/
|
||||
Haiku developers for indirectly giving me the idea
|
@ -1,106 +0,0 @@
|
||||
#include "dragonfail.h"
|
||||
#include "dragonfail_private.h"
|
||||
#include "dragonfail_error.h"
|
||||
|
||||
#ifdef DRAGONFAIL_BASIC_LOG
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#ifdef DRAGONFAIL_ABORT
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
// extern
|
||||
struct dgn dgn;
|
||||
|
||||
inline char** dgn_init()
|
||||
{
|
||||
#ifndef DRAGONFAIL_SKIP
|
||||
dgn.error = DGN_OK;
|
||||
return dgn.log;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void dgn_reset()
|
||||
{
|
||||
#ifndef DRAGONFAIL_SKIP
|
||||
dgn.error = DGN_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void dgn_basic_log()
|
||||
{
|
||||
#ifdef DRAGONFAIL_BASIC_LOG
|
||||
#ifndef DRAGONFAIL_SKIP
|
||||
if (dgn.error < DGN_SIZE)
|
||||
{
|
||||
fprintf(stderr, "%s\n", dgn.log[dgn.error]);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "%s\n", dgn.log[0]);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
inline char* dgn_output_log()
|
||||
{
|
||||
if (dgn.error < DGN_SIZE)
|
||||
{
|
||||
return dgn.log[dgn.error];
|
||||
}
|
||||
else
|
||||
{
|
||||
return dgn.log[0];
|
||||
}
|
||||
}
|
||||
|
||||
enum dgn_error dgn_output_code()
|
||||
{
|
||||
return dgn.error;
|
||||
}
|
||||
|
||||
#ifdef DRAGONFAIL_THROW_DEBUG_LOG
|
||||
inline void dgn_throw_extra(
|
||||
enum dgn_error new_code,
|
||||
const char* file,
|
||||
unsigned int line)
|
||||
#else
|
||||
inline void dgn_throw(
|
||||
enum dgn_error new_code)
|
||||
#endif
|
||||
{
|
||||
#ifndef DRAGONFAIL_SKIP
|
||||
dgn.error = new_code;
|
||||
|
||||
#ifdef DRAGONFAIL_THROW_BASIC_LOG
|
||||
#ifdef DRAGONFAIL_BASIC_LOG
|
||||
#ifdef DRAGONFAIL_THROW_DEBUG_LOG
|
||||
fprintf(
|
||||
stderr,
|
||||
"error in %s line %u: ",
|
||||
file,
|
||||
line);
|
||||
#endif
|
||||
|
||||
dgn_basic_log();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef DRAGONFAIL_ABORT
|
||||
abort();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
inline char dgn_catch()
|
||||
{
|
||||
#ifndef DRAGONFAIL_SKIP
|
||||
return (dgn.error != DGN_OK);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
#ifndef H_DRAGONFAIL
|
||||
#define H_DRAGONFAIL
|
||||
|
||||
#include "dragonfail_error.h"
|
||||
|
||||
#ifdef DRAGONFAIL_THROW_DEBUG_LOG
|
||||
#define dgn_throw(new_code) dgn_throw_extra(new_code, DGN_FILE, DGN_LINE)
|
||||
#define DGN_FILE __FILE__
|
||||
#define DGN_LINE __LINE__
|
||||
void dgn_throw_extra(enum dgn_error new_code, const char* file, unsigned int line);
|
||||
#else
|
||||
void dgn_throw(enum dgn_error new_code);
|
||||
#endif
|
||||
|
||||
char** dgn_init();
|
||||
void dgn_reset();
|
||||
void dgn_basic_log();
|
||||
char* dgn_output_log();
|
||||
enum dgn_error dgn_output_code();
|
||||
char dgn_catch();
|
||||
|
||||
#endif
|
@ -1,14 +0,0 @@
|
||||
#ifndef H_DRAGONFAIL_PRIVATE
|
||||
#define H_DRAGONFAIL_PRIVATE
|
||||
|
||||
#include "dragonfail_error.h"
|
||||
|
||||
struct dgn
|
||||
{
|
||||
enum dgn_error error;
|
||||
char* log[DGN_SIZE];
|
||||
};
|
||||
|
||||
extern struct dgn dgn;
|
||||
|
||||
#endif
|
@ -0,0 +1,84 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const TerminalBuffer = @import("../tui/TerminalBuffer.zig");
|
||||
const utils = @import("../tui/utils.zig");
|
||||
|
||||
const interop = @import("../interop.zig");
|
||||
const termbox = interop.termbox;
|
||||
|
||||
const Doom = @This();
|
||||
|
||||
pub const STEPS = 13;
|
||||
pub const FIRE = [_]termbox.tb_cell{
|
||||
utils.initCell(' ', 9, 0),
|
||||
utils.initCell(0x2591, 2, 0), // Red
|
||||
utils.initCell(0x2592, 2, 0), // Red
|
||||
utils.initCell(0x2593, 2, 0), // Red
|
||||
utils.initCell(0x2588, 2, 0), // Red
|
||||
utils.initCell(0x2591, 4, 2), // Yellow
|
||||
utils.initCell(0x2592, 4, 2), // Yellow
|
||||
utils.initCell(0x2593, 4, 2), // Yellow
|
||||
utils.initCell(0x2588, 4, 2), // Yellow
|
||||
utils.initCell(0x2591, 8, 4), // White
|
||||
utils.initCell(0x2592, 8, 4), // White
|
||||
utils.initCell(0x2593, 8, 4), // White
|
||||
utils.initCell(0x2588, 8, 4), // White
|
||||
};
|
||||
|
||||
allocator: Allocator,
|
||||
terminal_buffer: *TerminalBuffer,
|
||||
buffer: []u8,
|
||||
|
||||
pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer) !Doom {
|
||||
const buffer = try allocator.alloc(u8, terminal_buffer.width * terminal_buffer.height);
|
||||
initBuffer(buffer, terminal_buffer.width);
|
||||
|
||||
return .{
|
||||
.allocator = allocator,
|
||||
.terminal_buffer = terminal_buffer,
|
||||
.buffer = buffer,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: Doom) void {
|
||||
self.allocator.free(self.buffer);
|
||||
}
|
||||
|
||||
pub fn realloc(self: *Doom) !void {
|
||||
const buffer = try self.allocator.realloc(self.buffer, self.terminal_buffer.width * self.terminal_buffer.height);
|
||||
initBuffer(buffer, self.terminal_buffer.width);
|
||||
self.buffer = buffer;
|
||||
}
|
||||
|
||||
pub fn draw(self: Doom) void {
|
||||
for (0..self.terminal_buffer.width) |x| {
|
||||
for (1..self.terminal_buffer.height) |y| {
|
||||
const source = y * self.terminal_buffer.width + x;
|
||||
const random = (self.terminal_buffer.random.int(u16) % 7) & 3;
|
||||
|
||||
var dest = source - random + 1;
|
||||
if (self.terminal_buffer.width > dest) dest = 0 else dest -= self.terminal_buffer.width;
|
||||
|
||||
const buffer_source = self.buffer[source];
|
||||
const buffer_dest_offset = random & 1;
|
||||
|
||||
if (buffer_source < buffer_dest_offset) continue;
|
||||
|
||||
var buffer_dest = buffer_source - buffer_dest_offset;
|
||||
if (buffer_dest > 12) buffer_dest = 0;
|
||||
self.buffer[dest] = @intCast(buffer_dest);
|
||||
|
||||
self.terminal_buffer.buffer[dest] = FIRE[buffer_dest];
|
||||
self.terminal_buffer.buffer[source] = FIRE[buffer_source];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn initBuffer(buffer: []u8, width: u64) void {
|
||||
const length = buffer.len - width;
|
||||
const slice_start = buffer[0..length];
|
||||
const slice_end = buffer[length..];
|
||||
|
||||
@memset(slice_start, 0);
|
||||
@memset(slice_end, STEPS - 1);
|
||||
}
|
@ -0,0 +1,189 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const Random = std.rand.Random;
|
||||
const TerminalBuffer = @import("../tui/TerminalBuffer.zig");
|
||||
|
||||
const interop = @import("../interop.zig");
|
||||
const termbox = interop.termbox;
|
||||
|
||||
pub const FRAME_DELAY: u64 = 8;
|
||||
|
||||
// Allowed codepoints
|
||||
pub const MIN_CODEPOINT: isize = 33;
|
||||
pub const MAX_CODEPOINT: isize = 123 - MIN_CODEPOINT;
|
||||
|
||||
// Characters change mid-scroll
|
||||
pub const MID_SCROLL_CHANGE = true;
|
||||
|
||||
const Matrix = @This();
|
||||
|
||||
pub const Dot = struct {
|
||||
value: isize,
|
||||
is_head: bool,
|
||||
};
|
||||
|
||||
pub const Line = struct {
|
||||
space: isize,
|
||||
length: isize,
|
||||
update: isize,
|
||||
};
|
||||
|
||||
allocator: Allocator,
|
||||
terminal_buffer: *TerminalBuffer,
|
||||
dots: []Dot,
|
||||
lines: []Line,
|
||||
frame: u64,
|
||||
count: u64,
|
||||
|
||||
pub fn init(allocator: Allocator, terminal_buffer: *TerminalBuffer) !Matrix {
|
||||
const dots = try allocator.alloc(Dot, terminal_buffer.width * (terminal_buffer.height + 1));
|
||||
const lines = try allocator.alloc(Line, terminal_buffer.width);
|
||||
|
||||
initBuffers(dots, lines, terminal_buffer.width, terminal_buffer.height, terminal_buffer.random);
|
||||
|
||||
return .{
|
||||
.allocator = allocator,
|
||||
.terminal_buffer = terminal_buffer,
|
||||
.dots = dots,
|
||||
.lines = lines,
|
||||
.frame = 3,
|
||||
.count = 0,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: Matrix) void {
|
||||
self.allocator.free(self.dots);
|
||||
self.allocator.free(self.lines);
|
||||
}
|
||||
|
||||
pub fn realloc(self: *Matrix) !void {
|
||||
const dots = try self.allocator.realloc(self.dots, self.terminal_buffer.width * (self.terminal_buffer.height + 1));
|
||||
const lines = try self.allocator.realloc(self.lines, self.terminal_buffer.width);
|
||||
|
||||
initBuffers(dots, lines, self.terminal_buffer.width, self.terminal_buffer.height, self.terminal_buffer.random);
|
||||
|
||||
self.dots = dots;
|
||||
self.lines = lines;
|
||||
}
|
||||
|
||||
// TODO: Fix!!
|
||||
pub fn draw(self: *Matrix) void {
|
||||
var first_column = false;
|
||||
|
||||
self.count += 1;
|
||||
if (self.count > FRAME_DELAY) {
|
||||
self.frame += 1;
|
||||
if (self.frame > 4) self.frame = 1;
|
||||
self.count = 0;
|
||||
|
||||
var x: u64 = 0;
|
||||
while (x < self.terminal_buffer.width) : (x += 2) {
|
||||
var line = self.lines[x];
|
||||
if (self.frame <= line.update) continue;
|
||||
|
||||
var tail: u64 = 0;
|
||||
if (self.dots[x].value == -1 and self.dots[self.terminal_buffer.width + x].value == ' ') {
|
||||
if (line.space <= 0) {
|
||||
const random = self.terminal_buffer.random.int(i16);
|
||||
const h: isize = @intCast(self.terminal_buffer.height);
|
||||
line.length = @mod(random, h - 3) + 3;
|
||||
line.space = @mod(random, h) + 1;
|
||||
self.dots[x].value = @mod(random, MAX_CODEPOINT) + MIN_CODEPOINT;
|
||||
} else {
|
||||
line.space -= 1;
|
||||
}
|
||||
|
||||
self.lines[x] = line;
|
||||
first_column = true;
|
||||
|
||||
var y: u64 = 0;
|
||||
var seg_length: u64 = 0;
|
||||
|
||||
while (y <= self.terminal_buffer.height) : (y += 1) {
|
||||
// TODO: Are all these y/height checks required?
|
||||
var dot = self.dots[y * self.terminal_buffer.width + x];
|
||||
|
||||
// Skip over spaces
|
||||
while (dot.value == ' ' or dot.value == -1) {
|
||||
y += 1;
|
||||
if (y > self.terminal_buffer.height) break;
|
||||
|
||||
dot = self.dots[y * self.terminal_buffer.width + x];
|
||||
}
|
||||
if (y > self.terminal_buffer.height) break;
|
||||
|
||||
// Find the head of this column
|
||||
tail = y;
|
||||
seg_length = 0;
|
||||
while (y <= self.terminal_buffer.height and dot.value != ' ' and dot.value != -1) {
|
||||
dot.is_head = false;
|
||||
if (MID_SCROLL_CHANGE) {
|
||||
const random = self.terminal_buffer.random.int(i16);
|
||||
if (@mod(random, 8) == 0) dot.value = @mod(random, MAX_CODEPOINT) + MIN_CODEPOINT;
|
||||
}
|
||||
self.dots[y * self.terminal_buffer.width + x] = dot;
|
||||
|
||||
y += 1;
|
||||
seg_length += 1;
|
||||
dot = self.dots[y * self.terminal_buffer.width + x];
|
||||
}
|
||||
|
||||
// The head is down offscreen
|
||||
if (y > self.terminal_buffer.height) {
|
||||
self.dots[tail * self.terminal_buffer.width + x].value = ' ';
|
||||
continue; // TODO: Shouldn't this be break?
|
||||
}
|
||||
|
||||
const random = self.terminal_buffer.random.int(i16);
|
||||
self.dots[y * self.terminal_buffer.width + x].value = @mod(random, MAX_CODEPOINT) + MIN_CODEPOINT;
|
||||
self.dots[y * self.terminal_buffer.width + x].is_head = true;
|
||||
|
||||
if (seg_length > line.length or !first_column) {
|
||||
self.dots[tail * self.terminal_buffer.width + x].value = ' ';
|
||||
self.dots[x].value = -1;
|
||||
}
|
||||
first_column = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var x: u64 = 0;
|
||||
while (x < self.terminal_buffer.width) : (x += 2) {
|
||||
var y: u64 = 1;
|
||||
while (y <= self.terminal_buffer.height) : (y += 1) {
|
||||
const dot = self.dots[y * self.terminal_buffer.width + x];
|
||||
var fg: u32 = @intCast(termbox.TB_GREEN);
|
||||
|
||||
if (dot.value == -1 or dot.value == ' ') {
|
||||
termbox.tb_change_cell(@intCast(x), @intCast(y - 1), ' ', fg, termbox.TB_DEFAULT);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dot.is_head) fg = @intCast(termbox.TB_WHITE | termbox.TB_BOLD);
|
||||
termbox.tb_change_cell(@intCast(x), @intCast(y - 1), @intCast(dot.value), fg, termbox.TB_DEFAULT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn initBuffers(dots: []Dot, lines: []Line, width: u64, height: u64, random: Random) void {
|
||||
var y: u64 = 0;
|
||||
while (y <= height) : (y += 1) {
|
||||
var x: u64 = 0;
|
||||
while (x < width) : (x += 2) {
|
||||
dots[y * width + x].value = -1;
|
||||
}
|
||||
}
|
||||
|
||||
var x: u64 = 0;
|
||||
while (x < width) : (x += 2) {
|
||||
var line = lines[x];
|
||||
const h: isize = @intCast(height);
|
||||
line.space = @mod(random.int(i16), h) + 1;
|
||||
line.length = @mod(random.int(i16), h - 3) + 3;
|
||||
line.update = @mod(random.int(i16), 3) + 1;
|
||||
lines[x] = line;
|
||||
|
||||
dots[width + x].value = ' ';
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
const std = @import("std");
|
||||
const enums = @import("enums.zig");
|
||||
const interop = @import("interop.zig");
|
||||
const TerminalBuffer = @import("tui/TerminalBuffer.zig");
|
||||
const Desktop = @import("tui/components/Desktop.zig");
|
||||
const Text = @import("tui/components/Text.zig");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
// TODO
|
||||
pub fn authenticate(allocator: Allocator, tty: u8, buffer: TerminalBuffer, desktop: Desktop, login: Text, password: Text) !void {
|
||||
_ = buffer;
|
||||
|
||||
const uid = interop.getuid();
|
||||
|
||||
var tty_buffer = std.mem.zeroes([@sizeOf(u8) + 1]u8);
|
||||
var uid_buffer = std.mem.zeroes([10 + @sizeOf(u32) + 1]u8);
|
||||
|
||||
const tty_str = try std.fmt.bufPrintZ(&tty_buffer, "{d}", .{tty});
|
||||
const uid_str = try std.fmt.bufPrintZ(&uid_buffer, "/run/user/{d}", .{uid});
|
||||
const current_environment = desktop.environments.items[desktop.current];
|
||||
|
||||
// Add XDG environment variables
|
||||
setXdgSessionEnv(current_environment.display_server);
|
||||
try setXdgEnv(allocator, tty_str, uid_str, current_environment.xdg_name);
|
||||
|
||||
// Open the PAM session
|
||||
var credentials = [_][]const u8{ login.text.items, password.text.items };
|
||||
const conv = interop.pam.pam_conv{
|
||||
.conv = loginConv,
|
||||
.appdata_ptr = @ptrCast(&credentials),
|
||||
};
|
||||
_ = conv;
|
||||
}
|
||||
|
||||
fn setXdgSessionEnv(display_server: enums.DisplayServer) void {
|
||||
_ = interop.setenv("XDG_SESSION_TYPE", switch (display_server) {
|
||||
.wayland => "wayland",
|
||||
.shell => "tty",
|
||||
.xinitrc, .x11 => "x11",
|
||||
}, 0);
|
||||
}
|
||||
|
||||
fn setXdgEnv(allocator: Allocator, tty_str: [:0]u8, uid_str: [:0]u8, desktop_name: []const u8) !void {
|
||||
const desktop_name_z = try allocator.dupeZ(u8, desktop_name);
|
||||
defer allocator.free(desktop_name_z);
|
||||
|
||||
_ = interop.setenv("XDG_RUNTIME_DIR", uid_str, 0);
|
||||
_ = interop.setenv("XDG_SESSION_CLASS", "user", 0);
|
||||
_ = interop.setenv("XDG_SESSION_ID", "1", 0);
|
||||
_ = interop.setenv("XDG_SESSION_DESKTOP", desktop_name_z, 0);
|
||||
_ = interop.setenv("XDG_SEAT", "seat0", 0);
|
||||
_ = interop.setenv("XDG_VTNR", tty_str, 0);
|
||||
}
|
||||
|
||||
fn loginConv(
|
||||
num_msg: c_int,
|
||||
msg: [*][*]const interop.pam.pam_message,
|
||||
resp: [*][*]const interop.pam.pam_response,
|
||||
appdata_ptr: ?*anyopaque,
|
||||
) c_int {
|
||||
_ = num_msg;
|
||||
_ = msg;
|
||||
_ = resp;
|
||||
_ = appdata_ptr;
|
||||
}
|
@ -1,146 +0,0 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#define CLOCK_W 5
|
||||
#define CLOCK_H 5
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
#define X 0x2593
|
||||
#define _ 0x0000
|
||||
#else
|
||||
#define X '#'
|
||||
#define _ 0
|
||||
#endif
|
||||
|
||||
#if CLOCK_W == 5 && CLOCK_H == 5
|
||||
|
||||
uint32_t CLOCK_0[] = {
|
||||
X,X,X,X,X,
|
||||
X,X,_,X,X,
|
||||
X,X,_,X,X,
|
||||
X,X,_,X,X,
|
||||
X,X,X,X,X
|
||||
};
|
||||
|
||||
uint32_t CLOCK_1[] = {
|
||||
_,_,_,X,X,
|
||||
_,_,_,X,X,
|
||||
_,_,_,X,X,
|
||||
_,_,_,X,X,
|
||||
_,_,_,X,X
|
||||
};
|
||||
|
||||
uint32_t CLOCK_2[] = {
|
||||
X,X,X,X,X,
|
||||
_,_,_,X,X,
|
||||
X,X,X,X,X,
|
||||
X,X,_,_,_,
|
||||
X,X,X,X,X
|
||||
};
|
||||
|
||||
uint32_t CLOCK_3[] = {
|
||||
X,X,X,X,X,
|
||||
_,_,_,X,X,
|
||||
X,X,X,X,X,
|
||||
_,_,_,X,X,
|
||||
X,X,X,X,X
|
||||
};
|
||||
|
||||
uint32_t CLOCK_4[] = {
|
||||
X,X,_,X,X,
|
||||
X,X,_,X,X,
|
||||
X,X,X,X,X,
|
||||
_,_,_,X,X,
|
||||
_,_,_,X,X
|
||||
};
|
||||
|
||||
uint32_t CLOCK_5[] = {
|
||||
X,X,X,X,X,
|
||||
X,X,_,_,_,
|
||||
X,X,X,X,X,
|
||||
_,_,_,X,X,
|
||||
X,X,X,X,X
|
||||
};
|
||||
|
||||
uint32_t CLOCK_6[] = {
|
||||
X,X,X,X,X,
|
||||
X,X,_,_,_,
|
||||
X,X,X,X,X,
|
||||
X,X,_,X,X,
|
||||
X,X,X,X,X,
|
||||
};
|
||||
|
||||
uint32_t CLOCK_7[] = {
|
||||
X,X,X,X,X,
|
||||
_,_,_,X,X,
|
||||
_,_,_,X,X,
|
||||
_,_,_,X,X,
|
||||
_,_,_,X,X
|
||||
};
|
||||
|
||||
uint32_t CLOCK_8[] = {
|
||||
X,X,X,X,X,
|
||||
X,X,_,X,X,
|
||||
X,X,X,X,X,
|
||||
X,X,_,X,X,
|
||||
X,X,X,X,X
|
||||
};
|
||||
|
||||
uint32_t CLOCK_9[] = {
|
||||
X,X,X,X,X,
|
||||
X,X,_,X,X,
|
||||
X,X,X,X,X,
|
||||
_,_,_,X,X,
|
||||
X,X,X,X,X
|
||||
};
|
||||
|
||||
uint32_t CLOCK_S[] = {
|
||||
_,_,_,_,_,
|
||||
_,_,X,_,_,
|
||||
_,_,_,_,_,
|
||||
_,_,X,_,_,
|
||||
_,_,_,_,_
|
||||
};
|
||||
|
||||
uint32_t CLOCK_E[] = {
|
||||
_,_,_,_,_,
|
||||
_,_,_,_,_,
|
||||
_,_,_,_,_,
|
||||
_,_,_,_,_,
|
||||
_,_,_,_,_
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#undef X
|
||||
#undef _
|
||||
|
||||
static inline uint32_t* CLOCK_N(char c)
|
||||
{
|
||||
switch(c)
|
||||
{
|
||||
case '0':
|
||||
return CLOCK_0;
|
||||
case '1':
|
||||
return CLOCK_1;
|
||||
case '2':
|
||||
return CLOCK_2;
|
||||
case '3':
|
||||
return CLOCK_3;
|
||||
case '4':
|
||||
return CLOCK_4;
|
||||
case '5':
|
||||
return CLOCK_5;
|
||||
case '6':
|
||||
return CLOCK_6;
|
||||
case '7':
|
||||
return CLOCK_7;
|
||||
case '8':
|
||||
return CLOCK_8;
|
||||
case '9':
|
||||
return CLOCK_9;
|
||||
case ':':
|
||||
return CLOCK_S;
|
||||
default:
|
||||
return CLOCK_E;
|
||||
}
|
||||
}
|
@ -0,0 +1,140 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const interop = @import("interop.zig");
|
||||
const utils = @import("tui/utils.zig");
|
||||
|
||||
const termbox = interop.termbox;
|
||||
|
||||
const X: u32 = if (builtin.os.tag == .linux or builtin.os.tag.isBSD()) 0x2593 else '#';
|
||||
const O: u32 = 0;
|
||||
|
||||
pub const WIDTH: u64 = 5;
|
||||
pub const HEIGHT: u64 = 5;
|
||||
pub const SIZE = WIDTH * HEIGHT;
|
||||
|
||||
// zig fmt: off
|
||||
const ZERO = [_]u32{
|
||||
X,X,X,X,X,
|
||||
X,X,O,X,X,
|
||||
X,X,O,X,X,
|
||||
X,X,O,X,X,
|
||||
X,X,X,X,X,
|
||||
};
|
||||
const ONE = [_]u32{
|
||||
O,O,O,X,X,
|
||||
O,O,O,X,X,
|
||||
O,O,O,X,X,
|
||||
O,O,O,X,X,
|
||||
O,O,O,X,X,
|
||||
};
|
||||
const TWO = [_]u32{
|
||||
X,X,X,X,X,
|
||||
O,O,O,X,X,
|
||||
X,X,X,X,X,
|
||||
X,X,O,O,O,
|
||||
X,X,X,X,X,
|
||||
};
|
||||
const THREE = [_]u32{
|
||||
X,X,X,X,X,
|
||||
O,O,O,X,X,
|
||||
X,X,X,X,X,
|
||||
O,O,O,X,X,
|
||||
X,X,X,X,X,
|
||||
};
|
||||
const FOUR = [_]u32{
|
||||
X,X,O,X,X,
|
||||
X,X,O,X,X,
|
||||
X,X,X,X,X,
|
||||
O,O,O,X,X,
|
||||
O,O,O,X,X,
|
||||
};
|
||||
const FIVE = [_]u32{
|
||||
X,X,X,X,X,
|
||||
X,X,O,O,O,
|
||||
X,X,X,X,X,
|
||||
O,O,O,X,X,
|
||||
X,X,X,X,X,
|
||||
};
|
||||
const SIX = [_]u32{
|
||||
X,X,X,X,X,
|
||||
X,X,O,O,O,
|
||||
X,X,X,X,X,
|
||||
X,X,O,X,X,
|
||||
X,X,X,X,X,
|
||||
};
|
||||
const SEVEN = [_]u32{
|
||||
X,X,X,X,X,
|
||||
O,O,O,X,X,
|
||||
O,O,O,X,X,
|
||||
O,O,O,X,X,
|
||||
O,O,O,X,X,
|
||||
};
|
||||
const EIGHT = [_]u32{
|
||||
X,X,X,X,X,
|
||||
X,X,O,X,X,
|
||||
X,X,X,X,X,
|
||||
X,X,O,X,X,
|
||||
X,X,X,X,X,
|
||||
};
|
||||
const NINE = [_]u32{
|
||||
X,X,X,X,X,
|
||||
X,X,O,X,X,
|
||||
X,X,X,X,X,
|
||||
O,O,O,X,X,
|
||||
X,X,X,X,X,
|
||||
};
|
||||
const S = [_]u32{
|
||||
O,O,O,O,O,
|
||||
O,O,X,O,O,
|
||||
O,O,O,O,O,
|
||||
O,O,X,O,O,
|
||||
O,O,O,O,O,
|
||||
};
|
||||
const E = [_]u32{
|
||||
O,O,O,O,O,
|
||||
O,O,O,O,O,
|
||||
O,O,O,O,O,
|
||||
O,O,O,O,O,
|
||||
O,O,O,O,O,
|
||||
};
|
||||
// zig fmt: on
|
||||
|
||||
pub fn clockCell(animate: bool, char: u8, fg: u8, bg: u8) [SIZE]termbox.tb_cell {
|
||||
var cells = std.mem.zeroes([SIZE]termbox.tb_cell);
|
||||
|
||||
var tv = std.mem.zeroes(std.c.timeval);
|
||||
_ = std.c.gettimeofday(&tv, null);
|
||||
|
||||
const clock_chars = toBigNumber(if (animate and char == ':' and @divTrunc(tv.tv_usec, 500000) != 0) ' ' else char);
|
||||
for (0..cells.len) |i| cells[i] = utils.initCell(clock_chars[i], fg, bg);
|
||||
|
||||
return cells;
|
||||
}
|
||||
|
||||
pub fn alphaBlit(buffer: [*c]termbox.tb_cell, x: u64, y: u64, tb_width: u64, tb_height: u64, cells: [SIZE]termbox.tb_cell) void {
|
||||
if (x + WIDTH >= tb_width or y + HEIGHT >= tb_height) return;
|
||||
|
||||
for (0..HEIGHT) |yy| {
|
||||
for (0..WIDTH) |xx| {
|
||||
const cell = cells[yy * WIDTH + xx];
|
||||
if (cell.ch != 0) buffer[(y + yy) * tb_width + (x + xx)] = cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn toBigNumber(char: u8) []const u32 {
|
||||
return switch (char) {
|
||||
'0' => &ZERO,
|
||||
'1' => &ONE,
|
||||
'2' => &TWO,
|
||||
'3' => &THREE,
|
||||
'4' => &FOUR,
|
||||
'5' => &FIVE,
|
||||
'6' => &SIX,
|
||||
'7' => &SEVEN,
|
||||
'8' => &EIGHT,
|
||||
'9' => &NINE,
|
||||
':' => &S,
|
||||
else => &E,
|
||||
};
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
#ifndef H_LY_CONFIG
|
||||
#define H_LY_CONFIG
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct lang
|
||||
{
|
||||
char* capslock;
|
||||
char* err_alloc;
|
||||
char* err_bounds;
|
||||
char* err_chdir;
|
||||
char* err_console_dev;
|
||||
char* err_dgn_oob;
|
||||
char* err_domain;
|
||||
char* err_hostname;
|
||||
char* err_mlock;
|
||||
char* err_null;
|
||||
char* err_pam;
|
||||
char* err_pam_abort;
|
||||
char* err_pam_acct_expired;
|
||||
char* err_pam_auth;
|
||||
char* err_pam_authinfo_unavail;
|
||||
char* err_pam_authok_reqd;
|
||||
char* err_pam_buf;
|
||||
char* err_pam_cred_err;
|
||||
char* err_pam_cred_expired;
|
||||
char* err_pam_cred_insufficient;
|
||||
char* err_pam_cred_unavail;
|
||||
char* err_pam_maxtries;
|
||||
char* err_pam_perm_denied;
|
||||
char* err_pam_session;
|
||||
char* err_pam_sys;
|
||||
char* err_pam_user_unknown;
|
||||
char* err_path;
|
||||
char* err_perm_dir;
|
||||
char* err_perm_group;
|
||||
char* err_perm_user;
|
||||
char* err_pwnam;
|
||||
char* err_user_gid;
|
||||
char* err_user_init;
|
||||
char* err_user_uid;
|
||||
char* err_xsessions_dir;
|
||||
char* err_xsessions_open;
|
||||
char* f1;
|
||||
char* f2;
|
||||
char* login;
|
||||
char* logout;
|
||||
char* numlock;
|
||||
char* password;
|
||||
char* shell;
|
||||
char* wayland;
|
||||
char* xinitrc;
|
||||
};
|
||||
|
||||
struct config
|
||||
{
|
||||
bool animate;
|
||||
uint8_t animation;
|
||||
char asterisk;
|
||||
uint8_t bg;
|
||||
bool bigclock;
|
||||
bool blank_box;
|
||||
bool blank_password;
|
||||
char* clock;
|
||||
char* console_dev;
|
||||
uint8_t default_input;
|
||||
uint8_t fg;
|
||||
bool hide_borders;
|
||||
bool hide_f1_commands;
|
||||
uint8_t input_len;
|
||||
char* lang;
|
||||
bool load;
|
||||
uint8_t margin_box_h;
|
||||
uint8_t margin_box_v;
|
||||
uint8_t max_desktop_len;
|
||||
uint8_t max_login_len;
|
||||
uint8_t max_password_len;
|
||||
char* mcookie_cmd;
|
||||
uint16_t min_refresh_delta;
|
||||
char* path;
|
||||
char* restart_cmd;
|
||||
bool save;
|
||||
char* save_file;
|
||||
char* service_name;
|
||||
char* shutdown_cmd;
|
||||
char* term_reset_cmd;
|
||||
uint8_t tty;
|
||||
char* wayland_cmd;
|
||||
bool wayland_specifier;
|
||||
char* waylandsessions;
|
||||
char* x_cmd;
|
||||
char* xinitrc;
|
||||
char* x_cmd_setup;
|
||||
char* xauth_cmd;
|
||||
char* xsessions;
|
||||
};
|
||||
|
||||
extern struct lang lang;
|
||||
extern struct config config;
|
||||
|
||||
#endif
|
@ -1,424 +0,0 @@
|
||||
const std = @import("std");
|
||||
const ini = @import("ini");
|
||||
const main = @import("main.zig");
|
||||
const interop = @import("interop.zig");
|
||||
|
||||
const INI_CONFIG_PATH: []const u8 = "/etc/ly/";
|
||||
const INI_CONFIG_MAX_SIZE: usize = 16 * 1024;
|
||||
|
||||
pub const Inputs = enum {
|
||||
session,
|
||||
login,
|
||||
password,
|
||||
};
|
||||
|
||||
pub const LyConfig = struct {
|
||||
ly: struct {
|
||||
animate: bool,
|
||||
animation: u8,
|
||||
asterisk: u8,
|
||||
bg: u8,
|
||||
bigclock: bool,
|
||||
blank_box: bool,
|
||||
blank_password: bool,
|
||||
clock: []const u8,
|
||||
console_dev: []const u8,
|
||||
default_input: Inputs,
|
||||
fg: u8,
|
||||
hide_borders: bool,
|
||||
hide_f1_commands: bool,
|
||||
input_len: u8,
|
||||
lang: []const u8,
|
||||
load: bool,
|
||||
margin_box_h: u8,
|
||||
margin_box_v: u8,
|
||||
max_desktop_len: u8,
|
||||
max_login_len: u8,
|
||||
max_password_len: u8,
|
||||
mcookie_cmd: []const u8,
|
||||
min_refresh_delta: u16,
|
||||
path: []const u8,
|
||||
restart_cmd: []const u8,
|
||||
save: bool,
|
||||
save_file: []const u8,
|
||||
service_name: []const u8,
|
||||
shutdown_cmd: []const u8,
|
||||
term_reset_cmd: []const u8,
|
||||
tty: u8,
|
||||
wayland_cmd: []const u8,
|
||||
wayland_specifier: bool,
|
||||
waylandsessions: []const u8,
|
||||
x_cmd: []const u8,
|
||||
xinitrc: []const u8,
|
||||
x_cmd_setup: []const u8,
|
||||
xauth_cmd: []const u8,
|
||||
xsessions: []const u8,
|
||||
},
|
||||
};
|
||||
|
||||
pub const LyLang = struct {
|
||||
ly: struct {
|
||||
capslock: []const u8,
|
||||
err_alloc: []const u8,
|
||||
err_bounds: []const u8,
|
||||
err_chdir: []const u8,
|
||||
err_console_dev: []const u8,
|
||||
err_dgn_oob: []const u8,
|
||||
err_domain: []const u8,
|
||||
err_hostname: []const u8,
|
||||
err_mlock: []const u8,
|
||||
err_null: []const u8,
|
||||
err_pam: []const u8,
|
||||
err_pam_abort: []const u8,
|
||||
err_pam_acct_expired: []const u8,
|
||||
err_pam_auth: []const u8,
|
||||
err_pam_authinfo_unavail: []const u8,
|
||||
err_pam_authok_reqd: []const u8,
|
||||
err_pam_buf: []const u8,
|
||||
err_pam_cred_err: []const u8,
|
||||
err_pam_cred_expired: []const u8,
|
||||
err_pam_cred_insufficient: []const u8,
|
||||
err_pam_cred_unavail: []const u8,
|
||||
err_pam_maxtries: []const u8,
|
||||
err_pam_perm_denied: []const u8,
|
||||
err_pam_session: []const u8,
|
||||
err_pam_sys: []const u8,
|
||||
err_pam_user_unknown: []const u8,
|
||||
err_path: []const u8,
|
||||
err_perm_dir: []const u8,
|
||||
err_perm_group: []const u8,
|
||||
err_perm_user: []const u8,
|
||||
err_pwnam: []const u8,
|
||||
err_user_gid: []const u8,
|
||||
err_user_init: []const u8,
|
||||
err_user_uid: []const u8,
|
||||
err_xsessions_dir: []const u8,
|
||||
err_xsessions_open: []const u8,
|
||||
f1: []const u8,
|
||||
f2: []const u8,
|
||||
login: []const u8,
|
||||
logout: []const u8,
|
||||
numlock: []const u8,
|
||||
password: []const u8,
|
||||
shell: []const u8,
|
||||
wayland: []const u8,
|
||||
xinitrc: []const u8,
|
||||
},
|
||||
};
|
||||
|
||||
pub var ly_config: LyConfig = undefined;
|
||||
pub var ly_lang: LyLang = undefined;
|
||||
|
||||
var config_buffer: []u8 = undefined;
|
||||
var lang_buffer: []u8 = undefined;
|
||||
|
||||
var config_clock: [:0]u8 = undefined;
|
||||
var config_console_dev: [:0]u8 = undefined;
|
||||
var config_lang: [:0]u8 = undefined;
|
||||
var config_mcookie_cmd: [:0]u8 = undefined;
|
||||
var config_path: [:0]u8 = undefined;
|
||||
var config_restart_cmd: [:0]u8 = undefined;
|
||||
var config_save_file: [:0]u8 = undefined;
|
||||
var config_service_name: [:0]u8 = undefined;
|
||||
var config_shutdown_cmd: [:0]u8 = undefined;
|
||||
var config_term_reset_cmd: [:0]u8 = undefined;
|
||||
var config_wayland_cmd: [:0]u8 = undefined;
|
||||
var config_waylandsessions: [:0]u8 = undefined;
|
||||
var config_x_cmd: [:0]u8 = undefined;
|
||||
var config_xinitrc: [:0]u8 = undefined;
|
||||
var config_x_cmd_setup: [:0]u8 = undefined;
|
||||
var config_xauth_cmd: [:0]u8 = undefined;
|
||||
var config_xsessions: [:0]u8 = undefined;
|
||||
|
||||
var lang_capslock: [:0]u8 = undefined;
|
||||
var lang_err_alloc: [:0]u8 = undefined;
|
||||
var lang_err_bounds: [:0]u8 = undefined;
|
||||
var lang_err_chdir: [:0]u8 = undefined;
|
||||
var lang_err_console_dev: [:0]u8 = undefined;
|
||||
var lang_err_dgn_oob: [:0]u8 = undefined;
|
||||
var lang_err_domain: [:0]u8 = undefined;
|
||||
var lang_err_hostname: [:0]u8 = undefined;
|
||||
var lang_err_mlock: [:0]u8 = undefined;
|
||||
var lang_err_null: [:0]u8 = undefined;
|
||||
var lang_err_pam: [:0]u8 = undefined;
|
||||
var lang_err_pam_abort: [:0]u8 = undefined;
|
||||
var lang_err_pam_acct_expired: [:0]u8 = undefined;
|
||||
var lang_err_pam_auth: [:0]u8 = undefined;
|
||||
var lang_err_pam_authinfo_unavail: [:0]u8 = undefined;
|
||||
var lang_err_pam_authok_reqd: [:0]u8 = undefined;
|
||||
var lang_err_pam_buf: [:0]u8 = undefined;
|
||||
var lang_err_pam_cred_err: [:0]u8 = undefined;
|
||||
var lang_err_pam_cred_expired: [:0]u8 = undefined;
|
||||
var lang_err_pam_cred_insufficient: [:0]u8 = undefined;
|
||||
var lang_err_pam_cred_unavail: [:0]u8 = undefined;
|
||||
var lang_err_pam_maxtries: [:0]u8 = undefined;
|
||||
var lang_err_pam_perm_denied: [:0]u8 = undefined;
|
||||
var lang_err_pam_session: [:0]u8 = undefined;
|
||||
var lang_err_pam_sys: [:0]u8 = undefined;
|
||||
var lang_err_pam_user_unknown: [:0]u8 = undefined;
|
||||
var lang_err_path: [:0]u8 = undefined;
|
||||
var lang_err_perm_dir: [:0]u8 = undefined;
|
||||
var lang_err_perm_group: [:0]u8 = undefined;
|
||||
var lang_err_perm_user: [:0]u8 = undefined;
|
||||
var lang_err_pwnam: [:0]u8 = undefined;
|
||||
var lang_err_user_gid: [:0]u8 = undefined;
|
||||
var lang_err_user_init: [:0]u8 = undefined;
|
||||
var lang_err_user_uid: [:0]u8 = undefined;
|
||||
var lang_err_xsessions_dir: [:0]u8 = undefined;
|
||||
var lang_err_xsessions_open: [:0]u8 = undefined;
|
||||
var lang_f1: [:0]u8 = undefined;
|
||||
var lang_f2: [:0]u8 = undefined;
|
||||
var lang_login: [:0]u8 = undefined;
|
||||
var lang_logout: [:0]u8 = undefined;
|
||||
var lang_numlock: [:0]u8 = undefined;
|
||||
var lang_password: [:0]u8 = undefined;
|
||||
var lang_shell: [:0]u8 = undefined;
|
||||
var lang_wayland: [:0]u8 = undefined;
|
||||
var lang_xinitrc: [:0]u8 = undefined;
|
||||
|
||||
pub fn config_load(cfg_path: []const u8) !void {
|
||||
var file = try std.fs.cwd().openFile(if (cfg_path.len == 0) INI_CONFIG_PATH ++ "config.ini" else cfg_path, .{});
|
||||
defer file.close();
|
||||
|
||||
config_buffer = try main.allocator.alloc(u8, INI_CONFIG_MAX_SIZE);
|
||||
|
||||
var length = try file.readAll(config_buffer);
|
||||
|
||||
ly_config = try ini.readToStruct(LyConfig, config_buffer[0..length]);
|
||||
|
||||
config_clock = try interop.c_str(ly_config.ly.clock);
|
||||
config_console_dev = try interop.c_str(ly_config.ly.console_dev);
|
||||
config_lang = try interop.c_str(ly_config.ly.lang);
|
||||
config_mcookie_cmd = try interop.c_str(ly_config.ly.mcookie_cmd);
|
||||
config_path = try interop.c_str(ly_config.ly.path);
|
||||
config_restart_cmd = try interop.c_str(ly_config.ly.restart_cmd);
|
||||
config_save_file = try interop.c_str(ly_config.ly.save_file);
|
||||
config_service_name = try interop.c_str(ly_config.ly.service_name);
|
||||
config_shutdown_cmd = try interop.c_str(ly_config.ly.shutdown_cmd);
|
||||
config_term_reset_cmd = try interop.c_str(ly_config.ly.term_reset_cmd);
|
||||
config_wayland_cmd = try interop.c_str(ly_config.ly.wayland_cmd);
|
||||
config_waylandsessions = try interop.c_str(ly_config.ly.waylandsessions);
|
||||
config_x_cmd = try interop.c_str(ly_config.ly.x_cmd);
|
||||
config_xinitrc = try interop.c_str(ly_config.ly.xinitrc);
|
||||
config_x_cmd_setup = try interop.c_str(ly_config.ly.x_cmd_setup);
|
||||
config_xauth_cmd = try interop.c_str(ly_config.ly.xauth_cmd);
|
||||
config_xsessions = try interop.c_str(ly_config.ly.xsessions);
|
||||
|
||||
main.c_config.animate = ly_config.ly.animate;
|
||||
main.c_config.animation = ly_config.ly.animation;
|
||||
main.c_config.asterisk = ly_config.ly.asterisk;
|
||||
main.c_config.bg = ly_config.ly.bg;
|
||||
main.c_config.bigclock = ly_config.ly.bigclock;
|
||||
main.c_config.blank_box = ly_config.ly.blank_box;
|
||||
main.c_config.blank_password = ly_config.ly.blank_password;
|
||||
main.c_config.clock = config_clock.ptr;
|
||||
main.c_config.console_dev = config_console_dev.ptr;
|
||||
main.c_config.default_input = @intFromEnum(ly_config.ly.default_input);
|
||||
main.c_config.fg = ly_config.ly.fg;
|
||||
main.c_config.hide_borders = ly_config.ly.hide_borders;
|
||||
main.c_config.hide_f1_commands = ly_config.ly.hide_f1_commands;
|
||||
main.c_config.input_len = ly_config.ly.input_len;
|
||||
main.c_config.lang = config_lang.ptr;
|
||||
main.c_config.load = ly_config.ly.load;
|
||||
main.c_config.margin_box_h = ly_config.ly.margin_box_h;
|
||||
main.c_config.margin_box_v = ly_config.ly.margin_box_v;
|
||||
main.c_config.max_desktop_len = ly_config.ly.max_desktop_len;
|
||||
main.c_config.max_login_len = ly_config.ly.max_login_len;
|
||||
main.c_config.max_password_len = ly_config.ly.max_password_len;
|
||||
main.c_config.mcookie_cmd = config_mcookie_cmd.ptr;
|
||||
main.c_config.min_refresh_delta = ly_config.ly.min_refresh_delta;
|
||||
main.c_config.path = config_path.ptr;
|
||||
main.c_config.restart_cmd = config_restart_cmd.ptr;
|
||||
main.c_config.save = ly_config.ly.save;
|
||||
main.c_config.save_file = config_save_file.ptr;
|
||||
main.c_config.service_name = config_service_name.ptr;
|
||||
main.c_config.shutdown_cmd = config_shutdown_cmd.ptr;
|
||||
main.c_config.term_reset_cmd = config_term_reset_cmd.ptr;
|
||||
main.c_config.tty = ly_config.ly.tty;
|
||||
main.c_config.wayland_cmd = config_wayland_cmd.ptr;
|
||||
main.c_config.wayland_specifier = ly_config.ly.wayland_specifier;
|
||||
main.c_config.waylandsessions = config_waylandsessions.ptr;
|
||||
main.c_config.x_cmd = config_x_cmd.ptr;
|
||||
main.c_config.xinitrc = config_xinitrc.ptr;
|
||||
main.c_config.x_cmd_setup = config_x_cmd_setup.ptr;
|
||||
main.c_config.xauth_cmd = config_xauth_cmd.ptr;
|
||||
main.c_config.xsessions = config_xsessions.ptr;
|
||||
}
|
||||
|
||||
pub fn lang_load() !void {
|
||||
var path = try std.fmt.allocPrint(main.allocator, "{s}{s}.ini", .{ INI_CONFIG_PATH ++ "lang/", ly_config.ly.lang });
|
||||
defer main.allocator.free(path);
|
||||
|
||||
var file = try std.fs.cwd().openFile(path, .{});
|
||||
defer file.close();
|
||||
|
||||
lang_buffer = try main.allocator.alloc(u8, INI_CONFIG_MAX_SIZE);
|
||||
|
||||
var length = try file.readAll(lang_buffer);
|
||||
|
||||
ly_lang = try ini.readToStruct(LyLang, lang_buffer[0..length]);
|
||||
|
||||
lang_capslock = try interop.c_str(ly_lang.ly.capslock);
|
||||
lang_err_alloc = try interop.c_str(ly_lang.ly.err_alloc);
|
||||
lang_err_bounds = try interop.c_str(ly_lang.ly.err_bounds);
|
||||
lang_err_chdir = try interop.c_str(ly_lang.ly.err_chdir);
|
||||
lang_err_console_dev = try interop.c_str(ly_lang.ly.err_console_dev);
|
||||
lang_err_dgn_oob = try interop.c_str(ly_lang.ly.err_dgn_oob);
|
||||
lang_err_domain = try interop.c_str(ly_lang.ly.err_domain);
|
||||
lang_err_hostname = try interop.c_str(ly_lang.ly.err_hostname);
|
||||
lang_err_mlock = try interop.c_str(ly_lang.ly.err_mlock);
|
||||
lang_err_null = try interop.c_str(ly_lang.ly.err_null);
|
||||
lang_err_pam = try interop.c_str(ly_lang.ly.err_pam);
|
||||
lang_err_pam_abort = try interop.c_str(ly_lang.ly.err_pam_abort);
|
||||
lang_err_pam_acct_expired = try interop.c_str(ly_lang.ly.err_pam_acct_expired);
|
||||
lang_err_pam_auth = try interop.c_str(ly_lang.ly.err_pam_auth);
|
||||
lang_err_pam_authinfo_unavail = try interop.c_str(ly_lang.ly.err_pam_authinfo_unavail);
|
||||
lang_err_pam_authok_reqd = try interop.c_str(ly_lang.ly.err_pam_authok_reqd);
|
||||
lang_err_pam_buf = try interop.c_str(ly_lang.ly.err_pam_buf);
|
||||
lang_err_pam_cred_err = try interop.c_str(ly_lang.ly.err_pam_cred_err);
|
||||
lang_err_pam_cred_expired = try interop.c_str(ly_lang.ly.err_pam_cred_expired);
|
||||
lang_err_pam_cred_insufficient = try interop.c_str(ly_lang.ly.err_pam_cred_insufficient);
|
||||
lang_err_pam_cred_unavail = try interop.c_str(ly_lang.ly.err_pam_cred_unavail);
|
||||
lang_err_pam_maxtries = try interop.c_str(ly_lang.ly.err_pam_maxtries);
|
||||
lang_err_pam_perm_denied = try interop.c_str(ly_lang.ly.err_pam_perm_denied);
|
||||
lang_err_pam_session = try interop.c_str(ly_lang.ly.err_pam_session);
|
||||
lang_err_pam_sys = try interop.c_str(ly_lang.ly.err_pam_sys);
|
||||
lang_err_pam_user_unknown = try interop.c_str(ly_lang.ly.err_pam_user_unknown);
|
||||
lang_err_path = try interop.c_str(ly_lang.ly.err_path);
|
||||
lang_err_perm_dir = try interop.c_str(ly_lang.ly.err_perm_dir);
|
||||
lang_err_perm_group = try interop.c_str(ly_lang.ly.err_perm_group);
|
||||
lang_err_perm_user = try interop.c_str(ly_lang.ly.err_perm_user);
|
||||
lang_err_pwnam = try interop.c_str(ly_lang.ly.err_pwnam);
|
||||
lang_err_user_gid = try interop.c_str(ly_lang.ly.err_user_gid);
|
||||
lang_err_user_init = try interop.c_str(ly_lang.ly.err_user_init);
|
||||
lang_err_user_uid = try interop.c_str(ly_lang.ly.err_user_uid);
|
||||
lang_err_xsessions_dir = try interop.c_str(ly_lang.ly.err_xsessions_dir);
|
||||
lang_err_xsessions_open = try interop.c_str(ly_lang.ly.err_xsessions_open);
|
||||
lang_f1 = try interop.c_str(ly_lang.ly.f1);
|
||||
lang_f2 = try interop.c_str(ly_lang.ly.f2);
|
||||
lang_login = try interop.c_str(ly_lang.ly.login);
|
||||
lang_logout = try interop.c_str(ly_lang.ly.logout);
|
||||
lang_numlock = try interop.c_str(ly_lang.ly.numlock);
|
||||
lang_password = try interop.c_str(ly_lang.ly.password);
|
||||
lang_shell = try interop.c_str(ly_lang.ly.shell);
|
||||
lang_wayland = try interop.c_str(ly_lang.ly.wayland);
|
||||
lang_xinitrc = try interop.c_str(ly_lang.ly.xinitrc);
|
||||
|
||||
main.c_lang.capslock = lang_capslock.ptr;
|
||||
main.c_lang.err_alloc = lang_err_alloc.ptr;
|
||||
main.c_lang.err_bounds = lang_err_bounds.ptr;
|
||||
main.c_lang.err_chdir = lang_err_chdir.ptr;
|
||||
main.c_lang.err_console_dev = lang_err_console_dev.ptr;
|
||||
main.c_lang.err_dgn_oob = lang_err_dgn_oob.ptr;
|
||||
main.c_lang.err_domain = lang_err_domain.ptr;
|
||||
main.c_lang.err_hostname = lang_err_hostname.ptr;
|
||||
main.c_lang.err_mlock = lang_err_mlock.ptr;
|
||||
main.c_lang.err_null = lang_err_null.ptr;
|
||||
main.c_lang.err_pam = lang_err_pam.ptr;
|
||||
main.c_lang.err_pam_abort = lang_err_pam_abort.ptr;
|
||||
main.c_lang.err_pam_acct_expired = lang_err_pam_acct_expired.ptr;
|
||||
main.c_lang.err_pam_auth = lang_err_pam_auth.ptr;
|
||||
main.c_lang.err_pam_authinfo_unavail = lang_err_pam_authinfo_unavail.ptr;
|
||||
main.c_lang.err_pam_authok_reqd = lang_err_pam_authok_reqd.ptr;
|
||||
main.c_lang.err_pam_buf = lang_err_pam_buf.ptr;
|
||||
main.c_lang.err_pam_cred_err = lang_err_pam_cred_err.ptr;
|
||||
main.c_lang.err_pam_cred_expired = lang_err_pam_cred_expired.ptr;
|
||||
main.c_lang.err_pam_cred_insufficient = lang_err_pam_cred_insufficient.ptr;
|
||||
main.c_lang.err_pam_cred_unavail = lang_err_pam_cred_unavail.ptr;
|
||||
main.c_lang.err_pam_maxtries = lang_err_pam_maxtries.ptr;
|
||||
main.c_lang.err_pam_perm_denied = lang_err_pam_perm_denied.ptr;
|
||||
main.c_lang.err_pam_session = lang_err_pam_session.ptr;
|
||||
main.c_lang.err_pam_sys = lang_err_pam_sys.ptr;
|
||||
main.c_lang.err_pam_user_unknown = lang_err_pam_user_unknown.ptr;
|
||||
main.c_lang.err_path = lang_err_path.ptr;
|
||||
main.c_lang.err_perm_dir = lang_err_perm_dir.ptr;
|
||||
main.c_lang.err_perm_group = lang_err_perm_group.ptr;
|
||||
main.c_lang.err_perm_user = lang_err_perm_user.ptr;
|
||||
main.c_lang.err_pwnam = lang_err_pwnam.ptr;
|
||||
main.c_lang.err_user_gid = lang_err_user_gid.ptr;
|
||||
main.c_lang.err_user_init = lang_err_user_init.ptr;
|
||||
main.c_lang.err_user_uid = lang_err_user_uid.ptr;
|
||||
main.c_lang.err_xsessions_dir = lang_err_xsessions_dir.ptr;
|
||||
main.c_lang.err_xsessions_open = lang_err_xsessions_open.ptr;
|
||||
main.c_lang.f1 = lang_f1.ptr;
|
||||
main.c_lang.f2 = lang_f2.ptr;
|
||||
main.c_lang.login = lang_login.ptr;
|
||||
main.c_lang.logout = lang_logout.ptr;
|
||||
main.c_lang.numlock = lang_numlock.ptr;
|
||||
main.c_lang.password = lang_password.ptr;
|
||||
main.c_lang.shell = lang_shell.ptr;
|
||||
main.c_lang.wayland = lang_wayland.ptr;
|
||||
main.c_lang.xinitrc = lang_xinitrc.ptr;
|
||||
}
|
||||
|
||||
pub fn config_free() void {
|
||||
interop.allocator.free(config_clock);
|
||||
interop.allocator.free(config_console_dev);
|
||||
interop.allocator.free(config_lang);
|
||||
interop.allocator.free(config_mcookie_cmd);
|
||||
interop.allocator.free(config_path);
|
||||
interop.allocator.free(config_restart_cmd);
|
||||
interop.allocator.free(config_save_file);
|
||||
interop.allocator.free(config_service_name);
|
||||
interop.allocator.free(config_shutdown_cmd);
|
||||
interop.allocator.free(config_term_reset_cmd);
|
||||
interop.allocator.free(config_wayland_cmd);
|
||||
interop.allocator.free(config_waylandsessions);
|
||||
interop.allocator.free(config_x_cmd);
|
||||
interop.allocator.free(config_xinitrc);
|
||||
interop.allocator.free(config_x_cmd_setup);
|
||||
interop.allocator.free(config_xauth_cmd);
|
||||
interop.allocator.free(config_xsessions);
|
||||
|
||||
main.allocator.free(config_buffer);
|
||||
}
|
||||
|
||||
pub fn lang_free() void {
|
||||
interop.allocator.free(lang_capslock);
|
||||
interop.allocator.free(lang_err_alloc);
|
||||
interop.allocator.free(lang_err_bounds);
|
||||
interop.allocator.free(lang_err_chdir);
|
||||
interop.allocator.free(lang_err_console_dev);
|
||||
interop.allocator.free(lang_err_dgn_oob);
|
||||
interop.allocator.free(lang_err_domain);
|
||||
interop.allocator.free(lang_err_hostname);
|
||||
interop.allocator.free(lang_err_mlock);
|
||||
interop.allocator.free(lang_err_null);
|
||||
interop.allocator.free(lang_err_pam);
|
||||
interop.allocator.free(lang_err_pam_abort);
|
||||
interop.allocator.free(lang_err_pam_acct_expired);
|
||||
interop.allocator.free(lang_err_pam_auth);
|
||||
interop.allocator.free(lang_err_pam_authinfo_unavail);
|
||||
interop.allocator.free(lang_err_pam_authok_reqd);
|
||||
interop.allocator.free(lang_err_pam_buf);
|
||||
interop.allocator.free(lang_err_pam_cred_err);
|
||||
interop.allocator.free(lang_err_pam_cred_expired);
|
||||
interop.allocator.free(lang_err_pam_cred_insufficient);
|
||||
interop.allocator.free(lang_err_pam_cred_unavail);
|
||||
interop.allocator.free(lang_err_pam_maxtries);
|
||||
interop.allocator.free(lang_err_pam_perm_denied);
|
||||
interop.allocator.free(lang_err_pam_session);
|
||||
interop.allocator.free(lang_err_pam_sys);
|
||||
interop.allocator.free(lang_err_pam_user_unknown);
|
||||
interop.allocator.free(lang_err_path);
|
||||
interop.allocator.free(lang_err_perm_dir);
|
||||
interop.allocator.free(lang_err_perm_group);
|
||||
interop.allocator.free(lang_err_perm_user);
|
||||
interop.allocator.free(lang_err_pwnam);
|
||||
interop.allocator.free(lang_err_user_gid);
|
||||
interop.allocator.free(lang_err_user_init);
|
||||
interop.allocator.free(lang_err_user_uid);
|
||||
interop.allocator.free(lang_err_xsessions_dir);
|
||||
interop.allocator.free(lang_err_xsessions_open);
|
||||
interop.allocator.free(lang_f1);
|
||||
interop.allocator.free(lang_f2);
|
||||
interop.allocator.free(lang_login);
|
||||
interop.allocator.free(lang_logout);
|
||||
interop.allocator.free(lang_numlock);
|
||||
interop.allocator.free(lang_password);
|
||||
interop.allocator.free(lang_shell);
|
||||
interop.allocator.free(lang_wayland);
|
||||
interop.allocator.free(lang_xinitrc);
|
||||
|
||||
main.allocator.free(lang_buffer);
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
const build_options = @import("build_options");
|
||||
const enums = @import("../enums.zig");
|
||||
|
||||
const Animation = enums.Animation;
|
||||
const Input = enums.Input;
|
||||
|
||||
const Config = @This();
|
||||
|
||||
ly: struct {
|
||||
animation: Animation,
|
||||
asterisk: u8,
|
||||
bg: u8,
|
||||
bigclock: bool,
|
||||
blank_box: bool,
|
||||
blank_password: bool,
|
||||
clock: []const u8,
|
||||
console_dev: []const u8,
|
||||
default_input: Input,
|
||||
fg: u8,
|
||||
hide_borders: bool,
|
||||
hide_key_hints: bool,
|
||||
input_len: u8,
|
||||
lang: []const u8,
|
||||
load: bool,
|
||||
margin_box_h: u8,
|
||||
margin_box_v: u8,
|
||||
max_desktop_len: u8,
|
||||
max_login_len: u8,
|
||||
max_password_len: u8,
|
||||
mcookie_cmd: []const u8,
|
||||
min_refresh_delta: u16,
|
||||
path: []const u8,
|
||||
restart_cmd: []const u8,
|
||||
restart_key: []const u8,
|
||||
save: bool,
|
||||
save_file: []const u8,
|
||||
service_name: []const u8,
|
||||
shutdown_cmd: []const u8,
|
||||
shutdown_key: []const u8,
|
||||
term_reset_cmd: []const u8,
|
||||
term_restore_cursor_cmd: []const u8,
|
||||
tty: u8,
|
||||
wayland_cmd: []const u8,
|
||||
wayland_specifier: bool,
|
||||
waylandsessions: []const u8,
|
||||
x_cmd: []const u8,
|
||||
xinitrc: []const u8,
|
||||
x_cmd_setup: []const u8,
|
||||
xauth_cmd: []const u8,
|
||||
xsessions: []const u8,
|
||||
},
|
||||
|
||||
pub fn init() Config {
|
||||
return .{ .ly = .{
|
||||
.animation = .none,
|
||||
.asterisk = '*',
|
||||
.bg = 0,
|
||||
.bigclock = false,
|
||||
.blank_box = true,
|
||||
.blank_password = false,
|
||||
.clock = "",
|
||||
.console_dev = "/dev/console",
|
||||
.default_input = .login,
|
||||
.fg = 9,
|
||||
.hide_borders = false,
|
||||
.hide_key_hints = false,
|
||||
.input_len = 34,
|
||||
.lang = "en",
|
||||
.load = true,
|
||||
.margin_box_h = 2,
|
||||
.margin_box_v = 1,
|
||||
.max_desktop_len = 100,
|
||||
.max_login_len = 255,
|
||||
.max_password_len = 255,
|
||||
.mcookie_cmd = "/usr/bin/mcookie",
|
||||
.min_refresh_delta = 5,
|
||||
.path = "/sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin",
|
||||
.restart_cmd = "/sbin/shutdown -r now",
|
||||
.restart_key = "F2",
|
||||
.save = true,
|
||||
.save_file = "/etc/ly/save",
|
||||
.service_name = "ly",
|
||||
.shutdown_cmd = "/sbin/shutdown -a now",
|
||||
.shutdown_key = "F1",
|
||||
.term_reset_cmd = "/usr/bin/tput reset",
|
||||
.term_restore_cursor_cmd = "/usr/bin/tput cnorm",
|
||||
.tty = 2,
|
||||
.wayland_cmd = build_options.data_directory ++ "/wsetup.sh",
|
||||
.wayland_specifier = false,
|
||||
.waylandsessions = "/usr/share/wayland-sessions",
|
||||
.x_cmd = "/usr/bin/X",
|
||||
.xinitrc = "~/.xinitrc",
|
||||
.x_cmd_setup = build_options.data_directory ++ "/xsetup.sh",
|
||||
.xauth_cmd = "/usr/bin/xauth",
|
||||
.xsessions = "/usr/share/xsessions",
|
||||
} };
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
const std = @import("std");
|
||||
const ini = @import("ini");
|
||||
const Config = @import("Config.zig");
|
||||
const Lang = @import("Lang.zig");
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
pub const CONFIG_MAX_SIZE: u64 = 8 * 1024;
|
||||
|
||||
const ConfigReader = @This();
|
||||
|
||||
allocator: Allocator,
|
||||
config_allocated: bool = false,
|
||||
lang_allocated: bool = false,
|
||||
config: []u8 = undefined,
|
||||
lang: []u8 = undefined,
|
||||
|
||||
pub fn init(config_allocator: Allocator) ConfigReader {
|
||||
return .{
|
||||
.allocator = config_allocator,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: ConfigReader) void {
|
||||
if (self.config_allocated) self.allocator.free(self.config);
|
||||
if (self.lang_allocated) self.allocator.free(self.lang);
|
||||
}
|
||||
|
||||
pub fn readConfig(self: *ConfigReader, path: []const u8) !Config {
|
||||
var file = std.fs.cwd().openFile(path, .{}) catch return Config.init();
|
||||
defer file.close();
|
||||
|
||||
self.config = try file.readToEndAlloc(self.allocator, CONFIG_MAX_SIZE);
|
||||
self.config_allocated = true;
|
||||
|
||||
return try ini.readToStruct(Config, self.config);
|
||||
}
|
||||
|
||||
pub fn readLang(self: *ConfigReader, path: []const u8) !Lang {
|
||||
var file = std.fs.cwd().openFile(path, .{}) catch return Lang.init();
|
||||
defer file.close();
|
||||
|
||||
self.lang = try file.readToEndAlloc(self.allocator, CONFIG_MAX_SIZE);
|
||||
self.lang_allocated = true;
|
||||
|
||||
return try ini.readToStruct(Lang, self.lang);
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
const Lang = @This();
|
||||
|
||||
ly: struct {
|
||||
capslock: []const u8,
|
||||
err_alloc: []const u8,
|
||||
err_bounds: []const u8,
|
||||
err_chdir: []const u8,
|
||||
err_console_dev: []const u8,
|
||||
err_dgn_oob: []const u8,
|
||||
err_domain: []const u8,
|
||||
err_hostname: []const u8,
|
||||
err_mlock: []const u8,
|
||||
err_null: []const u8,
|
||||
err_pam: []const u8,
|
||||
err_pam_abort: []const u8,
|
||||
err_pam_acct_expired: []const u8,
|
||||
err_pam_auth: []const u8,
|
||||
err_pam_authinfo_unavail: []const u8,
|
||||
err_pam_authok_reqd: []const u8,
|
||||
err_pam_buf: []const u8,
|
||||
err_pam_cred_err: []const u8,
|
||||
err_pam_cred_expired: []const u8,
|
||||
err_pam_cred_insufficient: []const u8,
|
||||
err_pam_cred_unavail: []const u8,
|
||||
err_pam_maxtries: []const u8,
|
||||
err_pam_perm_denied: []const u8,
|
||||
err_pam_session: []const u8,
|
||||
err_pam_sys: []const u8,
|
||||
err_pam_user_unknown: []const u8,
|
||||
err_path: []const u8,
|
||||
err_perm_dir: []const u8,
|
||||
err_perm_group: []const u8,
|
||||
err_perm_user: []const u8,
|
||||
err_pwnam: []const u8,
|
||||
err_user_gid: []const u8,
|
||||
err_user_init: []const u8,
|
||||
err_user_uid: []const u8,
|
||||
err_xsessions_dir: []const u8,
|
||||
err_xsessions_open: []const u8,
|
||||
login: []const u8,
|
||||
logout: []const u8,
|
||||
numlock: []const u8,
|
||||
password: []const u8,
|
||||
restart: []const u8,
|
||||
shell: []const u8,
|
||||
shutdown: []const u8,
|
||||
wayland: []const u8,
|
||||
xinitrc: []const u8,
|
||||
},
|
||||
|
||||
pub fn init() Lang {
|
||||
return .{ .ly = .{
|
||||
.capslock = "capslock",
|
||||
.err_alloc = "failed memory allocation",
|
||||
.err_bounds = "out-of-bounds index",
|
||||
.err_chdir = "failed to open home folder",
|
||||
.err_console_dev = "failed to access console",
|
||||
.err_dgn_oob = "log message",
|
||||
.err_domain = "invalid domain",
|
||||
.err_hostname = "failed to get hostname",
|
||||
.err_mlock = "failed to lock password memory",
|
||||
.err_null = "null pointer",
|
||||
.err_pam = "pam transaction failed",
|
||||
.err_pam_abort = "pam transaction aborted",
|
||||
.err_pam_acct_expired = "account expired",
|
||||
.err_pam_auth = "authentication error",
|
||||
.err_pam_authinfo_unavail = "failed to get user info",
|
||||
.err_pam_authok_reqd = "token expired",
|
||||
.err_pam_buf = "memory buffer error",
|
||||
.err_pam_cred_err = "failed to set credentials",
|
||||
.err_pam_cred_expired = "credentials expired",
|
||||
.err_pam_cred_insufficient = "insufficient credentials",
|
||||
.err_pam_cred_unavail = "failed to get credentials",
|
||||
.err_pam_maxtries = "reached maximum tries limit",
|
||||
.err_pam_perm_denied = "permission denied",
|
||||
.err_pam_session = "session error",
|
||||
.err_pam_sys = "system error",
|
||||
.err_pam_user_unknown = "unknown user",
|
||||
.err_path = "failed to set path",
|
||||
.err_perm_dir = "failed to change current directory",
|
||||
.err_perm_group = "failed to downgrade group permissions",
|
||||
.err_perm_user = "failed to downgrade user permissions",
|
||||
.err_pwnam = "failed to get user info",
|
||||
.err_user_gid = "failed to set user GID",
|
||||
.err_user_init = "failed to initialize user",
|
||||
.err_user_uid = "failed to set user UID",
|
||||
.err_xsessions_dir = "failed to find sessions folder",
|
||||
.err_xsessions_open = "failed to open sessions folder",
|
||||
.login = "login:",
|
||||
.logout = "logged out",
|
||||
.numlock = "numlock",
|
||||
.password = "password:",
|
||||
.restart = "reboot",
|
||||
.shell = "shell",
|
||||
.shutdown = "shutdown",
|
||||
.wayland = "wayland",
|
||||
.xinitrc = "xinitrc",
|
||||
} };
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
#ifndef H_DRAGONFAIL_ERROR
|
||||
#define H_DRAGONFAIL_ERROR
|
||||
|
||||
enum dgn_error
|
||||
{
|
||||
DGN_OK, // do not remove
|
||||
|
||||
DGN_NULL,
|
||||
DGN_ALLOC,
|
||||
DGN_BOUNDS,
|
||||
DGN_DOMAIN,
|
||||
DGN_MLOCK,
|
||||
DGN_XSESSIONS_DIR,
|
||||
DGN_XSESSIONS_OPEN,
|
||||
DGN_PATH,
|
||||
DGN_CHDIR,
|
||||
DGN_PWNAM,
|
||||
DGN_USER_INIT,
|
||||
DGN_USER_GID,
|
||||
DGN_USER_UID,
|
||||
DGN_PAM,
|
||||
DGN_HOSTNAME,
|
||||
|
||||
DGN_SIZE, // do not remove
|
||||
};
|
||||
|
||||
#endif
|
@ -1,997 +0,0 @@
|
||||
#include "dragonfail.h"
|
||||
#include "termbox.h"
|
||||
|
||||
#include "inputs.h"
|
||||
#include "utils.h"
|
||||
#include "config.h"
|
||||
#include "draw.h"
|
||||
#include "bigclock.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
|
||||
#if defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
#include <sys/kbio.h>
|
||||
#else // linux
|
||||
#include <linux/kd.h>
|
||||
#endif
|
||||
|
||||
#define DOOM_STEPS 13
|
||||
|
||||
void draw_init(struct term_buf* buf)
|
||||
{
|
||||
buf->width = tb_width();
|
||||
buf->height = tb_height();
|
||||
hostname(&buf->info_line);
|
||||
|
||||
uint16_t len_login = strlen(lang.login);
|
||||
uint16_t len_password = strlen(lang.password);
|
||||
|
||||
if (len_login > len_password)
|
||||
{
|
||||
buf->labels_max_len = len_login;
|
||||
}
|
||||
else
|
||||
{
|
||||
buf->labels_max_len = len_password;
|
||||
}
|
||||
|
||||
buf->box_height = 7 + (2 * config.margin_box_v);
|
||||
buf->box_width =
|
||||
(2 * config.margin_box_h)
|
||||
+ (config.input_len + 1)
|
||||
+ buf->labels_max_len;
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
buf->box_chars.left_up = 0x250c;
|
||||
buf->box_chars.left_down = 0x2514;
|
||||
buf->box_chars.right_up = 0x2510;
|
||||
buf->box_chars.right_down = 0x2518;
|
||||
buf->box_chars.top = 0x2500;
|
||||
buf->box_chars.bot = 0x2500;
|
||||
buf->box_chars.left = 0x2502;
|
||||
buf->box_chars.right = 0x2502;
|
||||
#else
|
||||
buf->box_chars.left_up = '+';
|
||||
buf->box_chars.left_down = '+';
|
||||
buf->box_chars.right_up = '+';
|
||||
buf->box_chars.right_down= '+';
|
||||
buf->box_chars.top = '-';
|
||||
buf->box_chars.bot = '-';
|
||||
buf->box_chars.left = '|';
|
||||
buf->box_chars.right = '|';
|
||||
#endif
|
||||
}
|
||||
|
||||
static void doom_free(struct term_buf* buf);
|
||||
static void matrix_free(struct term_buf* buf);
|
||||
|
||||
void draw_free(struct term_buf* buf)
|
||||
{
|
||||
if (config.animate)
|
||||
{
|
||||
switch (config.animation)
|
||||
{
|
||||
case 0:
|
||||
doom_free(buf);
|
||||
break;
|
||||
case 1:
|
||||
matrix_free(buf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_box(struct term_buf* buf)
|
||||
{
|
||||
uint16_t box_x = (buf->width - buf->box_width) / 2;
|
||||
uint16_t box_y = (buf->height - buf->box_height) / 2;
|
||||
uint16_t box_x2 = (buf->width + buf->box_width) / 2;
|
||||
uint16_t box_y2 = (buf->height + buf->box_height) / 2;
|
||||
buf->box_x = box_x;
|
||||
buf->box_y = box_y;
|
||||
|
||||
if (!config.hide_borders)
|
||||
{
|
||||
// corners
|
||||
tb_change_cell(
|
||||
box_x - 1,
|
||||
box_y - 1,
|
||||
buf->box_chars.left_up,
|
||||
config.fg,
|
||||
config.bg);
|
||||
tb_change_cell(
|
||||
box_x2,
|
||||
box_y - 1,
|
||||
buf->box_chars.right_up,
|
||||
config.fg,
|
||||
config.bg);
|
||||
tb_change_cell(
|
||||
box_x - 1,
|
||||
box_y2,
|
||||
buf->box_chars.left_down,
|
||||
config.fg,
|
||||
config.bg);
|
||||
tb_change_cell(
|
||||
box_x2,
|
||||
box_y2,
|
||||
buf->box_chars.right_down,
|
||||
config.fg,
|
||||
config.bg);
|
||||
|
||||
// top and bottom
|
||||
struct tb_cell c1 = {buf->box_chars.top, config.fg, config.bg};
|
||||
struct tb_cell c2 = {buf->box_chars.bot, config.fg, config.bg};
|
||||
|
||||
for (uint16_t i = 0; i < buf->box_width; ++i)
|
||||
{
|
||||
tb_put_cell(
|
||||
box_x + i,
|
||||
box_y - 1,
|
||||
&c1);
|
||||
tb_put_cell(
|
||||
box_x + i,
|
||||
box_y2,
|
||||
&c2);
|
||||
}
|
||||
|
||||
// left and right
|
||||
c1.ch = buf->box_chars.left;
|
||||
c2.ch = buf->box_chars.right;
|
||||
|
||||
for (uint16_t i = 0; i < buf->box_height; ++i)
|
||||
{
|
||||
tb_put_cell(
|
||||
box_x - 1,
|
||||
box_y + i,
|
||||
&c1);
|
||||
|
||||
tb_put_cell(
|
||||
box_x2,
|
||||
box_y + i,
|
||||
&c2);
|
||||
}
|
||||
}
|
||||
|
||||
if (config.blank_box)
|
||||
{
|
||||
struct tb_cell blank = {' ', config.fg, config.bg};
|
||||
|
||||
for (uint16_t i = 0; i < buf->box_height; ++i)
|
||||
{
|
||||
for (uint16_t k = 0; k < buf->box_width; ++k)
|
||||
{
|
||||
tb_put_cell(
|
||||
box_x + k,
|
||||
box_y + i,
|
||||
&blank);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char* time_str(char* fmt, int maxlen)
|
||||
{
|
||||
time_t timer;
|
||||
char* buffer = malloc(maxlen);
|
||||
struct tm* tm_info;
|
||||
|
||||
timer = time(NULL);
|
||||
tm_info = localtime(&timer);
|
||||
|
||||
if (strftime(buffer, maxlen, fmt, tm_info) == 0)
|
||||
{
|
||||
buffer[0] = '\0';
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
extern inline uint32_t* CLOCK_N(char c);
|
||||
|
||||
struct tb_cell* clock_cell(char c)
|
||||
{
|
||||
struct tb_cell* cells = malloc(sizeof(struct tb_cell) * CLOCK_W * CLOCK_H);
|
||||
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
if (config.animate && c == ':' && tv.tv_usec / 500000)
|
||||
{
|
||||
c = ' ';
|
||||
}
|
||||
uint32_t* clockchars = CLOCK_N(c);
|
||||
|
||||
for (int i = 0; i < CLOCK_W * CLOCK_H; i++)
|
||||
{
|
||||
cells[i].ch = clockchars[i];
|
||||
cells[i].fg = config.fg;
|
||||
cells[i].bg = config.bg;
|
||||
}
|
||||
|
||||
return cells;
|
||||
}
|
||||
|
||||
void alpha_blit(struct tb_cell* buf, uint16_t x, uint16_t y, uint16_t w, uint16_t h, struct tb_cell* cells)
|
||||
{
|
||||
if (x + w >= tb_width() || y + h >= tb_height())
|
||||
return;
|
||||
|
||||
for (int i = 0; i < h; i++)
|
||||
{
|
||||
for (int j = 0; j < w; j++)
|
||||
{
|
||||
struct tb_cell cell = cells[i * w + j];
|
||||
if (cell.ch)
|
||||
{
|
||||
buf[(y + i) * tb_width() + (x + j)] = cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_bigclock(struct term_buf* buf)
|
||||
{
|
||||
if (!config.bigclock)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int xo = buf->width / 2 - (5 * (CLOCK_W + 1)) / 2;
|
||||
int yo = (buf->height - buf->box_height) / 2 - CLOCK_H - 2;
|
||||
|
||||
char* clockstr = time_str("%H:%M", 6);
|
||||
struct tb_cell* clockcell;
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
clockcell = clock_cell(clockstr[i]);
|
||||
alpha_blit(tb_cell_buffer(), xo + i * (CLOCK_W + 1), yo, CLOCK_W, CLOCK_H, clockcell);
|
||||
free(clockcell);
|
||||
}
|
||||
|
||||
free(clockstr);
|
||||
}
|
||||
|
||||
void draw_clock(struct term_buf* buf)
|
||||
{
|
||||
if (config.clock == NULL || strlen(config.clock) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char* clockstr = time_str(config.clock, 32);
|
||||
int clockstrlen = strlen(clockstr);
|
||||
|
||||
struct tb_cell* cells = strn_cell(clockstr, clockstrlen);
|
||||
tb_blit(buf->width - clockstrlen, 0, clockstrlen, 1, cells);
|
||||
|
||||
free(clockstr);
|
||||
free(cells);
|
||||
}
|
||||
|
||||
struct tb_cell* strn_cell(char* s, uint16_t len) // throws
|
||||
{
|
||||
struct tb_cell* cells = malloc((sizeof (struct tb_cell)) * len);
|
||||
char* s2 = s;
|
||||
uint32_t c;
|
||||
|
||||
if (cells != NULL)
|
||||
{
|
||||
for (uint16_t i = 0; i < len; ++i)
|
||||
{
|
||||
if ((s2 - s) >= len)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
s2 += utf8_char_to_unicode(&c, s2);
|
||||
|
||||
cells[i].ch = c;
|
||||
cells[i].bg = config.bg;
|
||||
cells[i].fg = config.fg;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dgn_throw(DGN_ALLOC);
|
||||
}
|
||||
|
||||
return cells;
|
||||
}
|
||||
|
||||
struct tb_cell* str_cell(char* s) // throws
|
||||
{
|
||||
return strn_cell(s, strlen(s));
|
||||
}
|
||||
|
||||
void draw_labels(struct term_buf* buf) // throws
|
||||
{
|
||||
// login text
|
||||
struct tb_cell* login = str_cell(lang.login);
|
||||
|
||||
if (dgn_catch())
|
||||
{
|
||||
dgn_reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
tb_blit(
|
||||
buf->box_x + config.margin_box_h,
|
||||
buf->box_y + config.margin_box_v + 4,
|
||||
strlen(lang.login),
|
||||
1,
|
||||
login);
|
||||
free(login);
|
||||
}
|
||||
|
||||
// password text
|
||||
struct tb_cell* password = str_cell(lang.password);
|
||||
|
||||
if (dgn_catch())
|
||||
{
|
||||
dgn_reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
tb_blit(
|
||||
buf->box_x + config.margin_box_h,
|
||||
buf->box_y + config.margin_box_v + 6,
|
||||
strlen(lang.password),
|
||||
1,
|
||||
password);
|
||||
free(password);
|
||||
}
|
||||
|
||||
if (buf->info_line != NULL)
|
||||
{
|
||||
uint16_t len = strlen(buf->info_line);
|
||||
struct tb_cell* info_cell = str_cell(buf->info_line);
|
||||
|
||||
if (dgn_catch())
|
||||
{
|
||||
dgn_reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
tb_blit(
|
||||
buf->box_x + ((buf->box_width - len) / 2),
|
||||
buf->box_y + config.margin_box_v,
|
||||
len,
|
||||
1,
|
||||
info_cell);
|
||||
free(info_cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_f_commands()
|
||||
{
|
||||
struct tb_cell* f1 = str_cell(lang.f1);
|
||||
|
||||
if (dgn_catch())
|
||||
{
|
||||
dgn_reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
tb_blit(0, 0, strlen(lang.f1), 1, f1);
|
||||
free(f1);
|
||||
}
|
||||
|
||||
struct tb_cell* f2 = str_cell(lang.f2);
|
||||
|
||||
if (dgn_catch())
|
||||
{
|
||||
dgn_reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
tb_blit(strlen(lang.f1) + 1, 0, strlen(lang.f2), 1, f2);
|
||||
free(f2);
|
||||
}
|
||||
}
|
||||
|
||||
void draw_lock_state(struct term_buf* buf)
|
||||
{
|
||||
// get values
|
||||
int fd = open(config.console_dev, O_RDONLY);
|
||||
|
||||
if (fd < 0)
|
||||
{
|
||||
buf->info_line = lang.err_console_dev;
|
||||
return;
|
||||
}
|
||||
|
||||
bool numlock_on;
|
||||
bool capslock_on;
|
||||
|
||||
#if defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
int led;
|
||||
ioctl(fd, KDGETLED, &led);
|
||||
numlock_on = led & LED_NUM;
|
||||
capslock_on = led & LED_CAP;
|
||||
#else // linux
|
||||
char led;
|
||||
ioctl(fd, KDGKBLED, &led);
|
||||
numlock_on = led & K_NUMLOCK;
|
||||
capslock_on = led & K_CAPSLOCK;
|
||||
#endif
|
||||
|
||||
close(fd);
|
||||
|
||||
// print text
|
||||
uint16_t pos_x = buf->width - strlen(lang.numlock);
|
||||
|
||||
if (numlock_on)
|
||||
{
|
||||
struct tb_cell* numlock = str_cell(lang.numlock);
|
||||
|
||||
if (dgn_catch())
|
||||
{
|
||||
dgn_reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
tb_blit(pos_x, 0, strlen(lang.numlock), 1, numlock);
|
||||
free(numlock);
|
||||
}
|
||||
}
|
||||
|
||||
pos_x -= strlen(lang.capslock) + 1;
|
||||
|
||||
if (capslock_on)
|
||||
{
|
||||
struct tb_cell* capslock = str_cell(lang.capslock);
|
||||
|
||||
if (dgn_catch())
|
||||
{
|
||||
dgn_reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
tb_blit(pos_x, 0, strlen(lang.capslock), 1, capslock);
|
||||
free(capslock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_desktop(struct desktop* target)
|
||||
{
|
||||
uint16_t len = strlen(target->list[target->cur]);
|
||||
|
||||
if (len > (target->visible_len - 3))
|
||||
{
|
||||
len = target->visible_len - 3;
|
||||
}
|
||||
|
||||
tb_change_cell(
|
||||
target->x,
|
||||
target->y,
|
||||
'<',
|
||||
config.fg,
|
||||
config.bg);
|
||||
|
||||
tb_change_cell(
|
||||
target->x + target->visible_len - 1,
|
||||
target->y,
|
||||
'>',
|
||||
config.fg,
|
||||
config.bg);
|
||||
|
||||
for (uint16_t i = 0; i < len; ++ i)
|
||||
{
|
||||
tb_change_cell(
|
||||
target->x + i + 2,
|
||||
target->y,
|
||||
target->list[target->cur][i],
|
||||
config.fg,
|
||||
config.bg);
|
||||
}
|
||||
}
|
||||
|
||||
void draw_input(struct text* input)
|
||||
{
|
||||
uint16_t len = strlen(input->text);
|
||||
uint16_t visible_len = input->visible_len;
|
||||
|
||||
if (len > visible_len)
|
||||
{
|
||||
len = visible_len;
|
||||
}
|
||||
|
||||
struct tb_cell* cells = strn_cell(input->visible_start, len);
|
||||
|
||||
if (dgn_catch())
|
||||
{
|
||||
dgn_reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
tb_blit(input->x, input->y, len, 1, cells);
|
||||
free(cells);
|
||||
|
||||
struct tb_cell c1 = {' ', config.fg, config.bg};
|
||||
|
||||
for (uint16_t i = input->end - input->visible_start; i < visible_len; ++i)
|
||||
{
|
||||
tb_put_cell(
|
||||
input->x + i,
|
||||
input->y,
|
||||
&c1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_input_mask(struct text* input)
|
||||
{
|
||||
uint16_t len = strlen(input->text);
|
||||
uint16_t visible_len = input->visible_len;
|
||||
|
||||
if (len > visible_len)
|
||||
{
|
||||
len = visible_len;
|
||||
}
|
||||
|
||||
struct tb_cell c1 = {config.asterisk, config.fg, config.bg};
|
||||
struct tb_cell c2 = {' ', config.fg, config.bg};
|
||||
|
||||
for (uint16_t i = 0; i < visible_len; ++i)
|
||||
{
|
||||
if (input->visible_start + i < input->end)
|
||||
{
|
||||
tb_put_cell(
|
||||
input->x + i,
|
||||
input->y,
|
||||
&c1);
|
||||
}
|
||||
else
|
||||
{
|
||||
tb_put_cell(
|
||||
input->x + i,
|
||||
input->y,
|
||||
&c2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void position_input(
|
||||
struct term_buf* buf,
|
||||
struct desktop* desktop,
|
||||
struct text* login,
|
||||
struct text* password)
|
||||
{
|
||||
uint16_t x = buf->box_x + config.margin_box_h + buf->labels_max_len + 1;
|
||||
int32_t len = buf->box_x + buf->box_width - config.margin_box_h - x;
|
||||
|
||||
if (len < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
desktop->x = x;
|
||||
desktop->y = buf->box_y + config.margin_box_v + 2;
|
||||
desktop->visible_len = len;
|
||||
|
||||
login->x = x;
|
||||
login->y = buf->box_y + config.margin_box_v + 4;
|
||||
login->visible_len = len;
|
||||
|
||||
password->x = x;
|
||||
password->y = buf->box_y + config.margin_box_v + 6;
|
||||
password->visible_len = len;
|
||||
}
|
||||
|
||||
static void doom_init(struct term_buf* buf)
|
||||
{
|
||||
buf->init_width = buf->width;
|
||||
buf->init_height = buf->height;
|
||||
buf->astate.doom = malloc(sizeof(struct doom_state));
|
||||
|
||||
if (buf->astate.doom == NULL)
|
||||
{
|
||||
dgn_throw(DGN_ALLOC);
|
||||
}
|
||||
|
||||
uint16_t tmp_len = buf->width * buf->height;
|
||||
buf->astate.doom->buf = malloc(tmp_len);
|
||||
tmp_len -= buf->width;
|
||||
|
||||
if (buf->astate.doom->buf == NULL)
|
||||
{
|
||||
dgn_throw(DGN_ALLOC);
|
||||
}
|
||||
|
||||
memset(buf->astate.doom->buf, 0, tmp_len);
|
||||
memset(buf->astate.doom->buf + tmp_len, DOOM_STEPS - 1, buf->width);
|
||||
}
|
||||
|
||||
static void doom_free(struct term_buf* buf)
|
||||
{
|
||||
free(buf->astate.doom->buf);
|
||||
free(buf->astate.doom);
|
||||
}
|
||||
|
||||
// Adapted from cmatrix
|
||||
static void matrix_init(struct term_buf* buf)
|
||||
{
|
||||
buf->init_width = buf->width;
|
||||
buf->init_height = buf->height;
|
||||
buf->astate.matrix = malloc(sizeof(struct matrix_state));
|
||||
struct matrix_state* s = buf->astate.matrix;
|
||||
|
||||
if (s == NULL)
|
||||
{
|
||||
dgn_throw(DGN_ALLOC);
|
||||
}
|
||||
|
||||
uint16_t len = buf->height + 1;
|
||||
s->grid = malloc(sizeof(struct matrix_dot*) * len);
|
||||
|
||||
if (s->grid == NULL)
|
||||
{
|
||||
dgn_throw(DGN_ALLOC);
|
||||
}
|
||||
|
||||
len = (buf->height + 1) * buf->width;
|
||||
(s->grid)[0] = malloc(sizeof(struct matrix_dot) * len);
|
||||
|
||||
if ((s->grid)[0] == NULL)
|
||||
{
|
||||
dgn_throw(DGN_ALLOC);
|
||||
}
|
||||
|
||||
for (int i = 1; i <= buf->height; ++i)
|
||||
{
|
||||
s->grid[i] = s->grid[i - 1] + buf->width;
|
||||
|
||||
if (s->grid[i] == NULL)
|
||||
{
|
||||
dgn_throw(DGN_ALLOC);
|
||||
}
|
||||
}
|
||||
|
||||
s->length = malloc(buf->width * sizeof(int));
|
||||
|
||||
if (s->length == NULL)
|
||||
{
|
||||
dgn_throw(DGN_ALLOC);
|
||||
}
|
||||
|
||||
s->spaces = malloc(buf->width * sizeof(int));
|
||||
|
||||
if (s->spaces == NULL)
|
||||
{
|
||||
dgn_throw(DGN_ALLOC);
|
||||
}
|
||||
|
||||
s->updates = malloc(buf->width * sizeof(int));
|
||||
|
||||
if (s->updates == NULL)
|
||||
{
|
||||
dgn_throw(DGN_ALLOC);
|
||||
}
|
||||
|
||||
// Initialize grid
|
||||
for (int i = 0; i <= buf->height; ++i)
|
||||
{
|
||||
for (int j = 0; j <= buf->width - 1; j += 2)
|
||||
{
|
||||
s->grid[i][j].val = -1;
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < buf->width; j += 2)
|
||||
{
|
||||
s->spaces[j] = (int) rand() % buf->height + 1;
|
||||
s->length[j] = (int) rand() % (buf->height - 3) + 3;
|
||||
s->grid[1][j].val = ' ';
|
||||
s->updates[j] = (int) rand() % 3 + 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void matrix_free(struct term_buf* buf)
|
||||
{
|
||||
free(buf->astate.matrix->grid[0]);
|
||||
free(buf->astate.matrix->grid);
|
||||
free(buf->astate.matrix->length);
|
||||
free(buf->astate.matrix->spaces);
|
||||
free(buf->astate.matrix->updates);
|
||||
free(buf->astate.matrix);
|
||||
}
|
||||
|
||||
void animate_init(struct term_buf* buf)
|
||||
{
|
||||
if (config.animate)
|
||||
{
|
||||
switch(config.animation)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
doom_init(buf);
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
matrix_init(buf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void doom(struct term_buf* term_buf)
|
||||
{
|
||||
static struct tb_cell fire[DOOM_STEPS] =
|
||||
{
|
||||
{' ', 9, 0}, // default
|
||||
{0x2591, 2, 0}, // red
|
||||
{0x2592, 2, 0}, // red
|
||||
{0x2593, 2, 0}, // red
|
||||
{0x2588, 2, 0}, // red
|
||||
{0x2591, 4, 2}, // yellow
|
||||
{0x2592, 4, 2}, // yellow
|
||||
{0x2593, 4, 2}, // yellow
|
||||
{0x2588, 4, 2}, // yellow
|
||||
{0x2591, 8, 4}, // white
|
||||
{0x2592, 8, 4}, // white
|
||||
{0x2593, 8, 4}, // white
|
||||
{0x2588, 8, 4}, // white
|
||||
};
|
||||
|
||||
uint16_t src;
|
||||
uint16_t random;
|
||||
uint16_t dst;
|
||||
|
||||
uint16_t w = term_buf->init_width;
|
||||
uint8_t* tmp = term_buf->astate.doom->buf;
|
||||
|
||||
if ((term_buf->width != term_buf->init_width) || (term_buf->height != term_buf->init_height))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
struct tb_cell* buf = tb_cell_buffer();
|
||||
|
||||
for (uint16_t x = 0; x < w; ++x)
|
||||
{
|
||||
for (uint16_t y = 1; y < term_buf->init_height; ++y)
|
||||
{
|
||||
src = y * w + x;
|
||||
random = ((rand() % 7) & 3);
|
||||
dst = src - random + 1;
|
||||
|
||||
if (w > dst)
|
||||
{
|
||||
dst = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
dst -= w;
|
||||
}
|
||||
|
||||
tmp[dst] = tmp[src] - (random & 1);
|
||||
|
||||
if (tmp[dst] > 12)
|
||||
{
|
||||
tmp[dst] = 0;
|
||||
}
|
||||
|
||||
buf[dst] = fire[tmp[dst]];
|
||||
buf[src] = fire[tmp[src]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Adapted from cmatrix
|
||||
static void matrix(struct term_buf* buf)
|
||||
{
|
||||
static int frame = 3;
|
||||
const int frame_delay = 8;
|
||||
static int count = 0;
|
||||
bool first_col;
|
||||
struct matrix_state* s = buf->astate.matrix;
|
||||
|
||||
// Allowed codepoints
|
||||
const int randmin = 33;
|
||||
const int randnum = 123 - randmin;
|
||||
// Chars change mid-scroll
|
||||
const bool changes = true;
|
||||
|
||||
if ((buf->width != buf->init_width) || (buf->height != buf->init_height))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
count += 1;
|
||||
if (count > frame_delay)
|
||||
{
|
||||
frame += 1;
|
||||
if (frame > 4) frame = 1;
|
||||
count = 0;
|
||||
|
||||
for (int j = 0; j < buf->width; j += 2)
|
||||
{
|
||||
int tail;
|
||||
if (frame > s->updates[j])
|
||||
{
|
||||
if (s->grid[0][j].val == -1 && s->grid[1][j].val == ' ')
|
||||
{
|
||||
if (s->spaces[j] > 0)
|
||||
{
|
||||
s->spaces[j]--;
|
||||
} else {
|
||||
s->length[j] = (int) rand() % (buf->height - 3) + 3;
|
||||
s->grid[0][j].val = (int) rand() % randnum + randmin;
|
||||
s->spaces[j] = (int) rand() % buf->height + 1;
|
||||
}
|
||||
}
|
||||
|
||||
int i = 0, seg_len = 0;
|
||||
first_col = 1;
|
||||
while (i <= buf->height)
|
||||
{
|
||||
// Skip over spaces
|
||||
while (i <= buf->height
|
||||
&& (s->grid[i][j].val == ' ' || s->grid[i][j].val == -1))
|
||||
{
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i > buf->height) break;
|
||||
|
||||
// Find the head of this col
|
||||
tail = i;
|
||||
seg_len = 0;
|
||||
while (i <= buf->height
|
||||
&& (s->grid[i][j].val != ' ' && s->grid[i][j].val != -1))
|
||||
{
|
||||
s->grid[i][j].is_head = false;
|
||||
if (changes)
|
||||
{
|
||||
if (rand() % 8 == 0)
|
||||
s->grid[i][j].val = (int) rand() % randnum + randmin;
|
||||
}
|
||||
i++;
|
||||
seg_len++;
|
||||
}
|
||||
|
||||
// Head's down offscreen
|
||||
if (i > buf->height)
|
||||
{
|
||||
s->grid[tail][j].val = ' ';
|
||||
continue;
|
||||
}
|
||||
|
||||
s->grid[i][j].val = (int) rand() % randnum + randmin;
|
||||
s->grid[i][j].is_head = true;
|
||||
|
||||
if (seg_len > s->length[j] || !first_col) {
|
||||
s->grid[tail][j].val = ' ';
|
||||
s->grid[0][j].val = -1;
|
||||
}
|
||||
first_col = 0;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t blank;
|
||||
utf8_char_to_unicode(&blank, " ");
|
||||
|
||||
for (int j = 0; j < buf->width; j += 2)
|
||||
{
|
||||
for (int i = 1; i <= buf->height; ++i)
|
||||
{
|
||||
uint32_t c;
|
||||
int fg = TB_GREEN;
|
||||
int bg = TB_DEFAULT;
|
||||
|
||||
if (s->grid[i][j].val == -1 || s->grid[i][j].val == ' ')
|
||||
{
|
||||
tb_change_cell(j, i - 1, blank, fg, bg);
|
||||
continue;
|
||||
}
|
||||
|
||||
char tmp[2];
|
||||
tmp[0] = s->grid[i][j].val;
|
||||
tmp[1] = '\0';
|
||||
if(utf8_char_to_unicode(&c, tmp))
|
||||
{
|
||||
if (s->grid[i][j].is_head)
|
||||
{
|
||||
fg = TB_WHITE | TB_BOLD;
|
||||
}
|
||||
tb_change_cell(j, i - 1, c, fg, bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void animate(struct term_buf* buf)
|
||||
{
|
||||
buf->width = tb_width();
|
||||
buf->height = tb_height();
|
||||
|
||||
if (config.animate)
|
||||
{
|
||||
switch(config.animation)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
doom(buf);
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
matrix(buf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool cascade(struct term_buf* term_buf, uint8_t* fails)
|
||||
{
|
||||
uint16_t width = term_buf->width;
|
||||
uint16_t height = term_buf->height;
|
||||
|
||||
struct tb_cell* buf = tb_cell_buffer();
|
||||
bool changes = false;
|
||||
char c_under;
|
||||
char c;
|
||||
|
||||
for (int i = height - 2; i >= 0; --i)
|
||||
{
|
||||
for (int k = 0; k < width; ++k)
|
||||
{
|
||||
c = buf[i * width + k].ch;
|
||||
|
||||
if (isspace(c))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
c_under = buf[(i + 1) * width + k].ch;
|
||||
|
||||
if (!isspace(c_under))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!changes)
|
||||
{
|
||||
changes = true;
|
||||
}
|
||||
|
||||
if ((rand() % 10) > 7)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
buf[(i + 1) * width + k] = buf[i * width + k];
|
||||
buf[i * width + k].ch = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
// stop force-updating
|
||||
if (!changes)
|
||||
{
|
||||
sleep(7);
|
||||
*fails = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// force-update
|
||||
return true;
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
#ifndef H_LY_DRAW
|
||||
#define H_LY_DRAW
|
||||
|
||||
#include "termbox.h"
|
||||
#include "inputs.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct box
|
||||
{
|
||||
uint32_t left_up;
|
||||
uint32_t left_down;
|
||||
uint32_t right_up;
|
||||
uint32_t right_down;
|
||||
uint32_t top;
|
||||
uint32_t bot;
|
||||
uint32_t left;
|
||||
uint32_t right;
|
||||
};
|
||||
|
||||
struct matrix_dot
|
||||
{
|
||||
int val;
|
||||
bool is_head;
|
||||
};
|
||||
|
||||
struct matrix_state
|
||||
{
|
||||
struct matrix_dot** grid;
|
||||
int* length;
|
||||
int* spaces;
|
||||
int* updates;
|
||||
};
|
||||
|
||||
struct doom_state
|
||||
{
|
||||
uint8_t* buf;
|
||||
};
|
||||
|
||||
union anim_state
|
||||
{
|
||||
struct doom_state* doom;
|
||||
struct matrix_state* matrix;
|
||||
};
|
||||
|
||||
struct term_buf
|
||||
{
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint16_t init_width;
|
||||
uint16_t init_height;
|
||||
|
||||
struct box box_chars;
|
||||
char* info_line;
|
||||
uint16_t labels_max_len;
|
||||
uint16_t box_x;
|
||||
uint16_t box_y;
|
||||
uint16_t box_width;
|
||||
uint16_t box_height;
|
||||
|
||||
union anim_state astate;
|
||||
};
|
||||
|
||||
void draw_init(struct term_buf* buf);
|
||||
void draw_free(struct term_buf* buf);
|
||||
void draw_box(struct term_buf* buf);
|
||||
|
||||
struct tb_cell* strn_cell(char* s, uint16_t len);
|
||||
struct tb_cell* str_cell(char* s);
|
||||
|
||||
void draw_labels(struct term_buf* buf);
|
||||
void draw_f_commands();
|
||||
void draw_lock_state(struct term_buf* buf);
|
||||
void draw_desktop(struct desktop* target);
|
||||
void draw_input(struct text* input);
|
||||
void draw_input_mask(struct text* input);
|
||||
|
||||
void position_input(
|
||||
struct term_buf* buf,
|
||||
struct desktop* desktop,
|
||||
struct text* login,
|
||||
struct text* password);
|
||||
|
||||
void animate_init(struct term_buf* buf);
|
||||
void animate(struct term_buf* buf);
|
||||
bool cascade(struct term_buf* buf, uint8_t* fails);
|
||||
|
||||
void draw_bigclock(struct term_buf *buf);
|
||||
void draw_clock(struct term_buf *buf);
|
||||
|
||||
#endif
|
@ -0,0 +1,18 @@
|
||||
pub const Animation = enum {
|
||||
none,
|
||||
doom,
|
||||
matrix,
|
||||
};
|
||||
|
||||
pub const DisplayServer = enum {
|
||||
wayland,
|
||||
shell,
|
||||
xinitrc,
|
||||
x11,
|
||||
};
|
||||
|
||||
pub const Input = enum {
|
||||
session,
|
||||
login,
|
||||
password,
|
||||
};
|
@ -1,285 +0,0 @@
|
||||
#include "dragonfail.h"
|
||||
#include "termbox.h"
|
||||
#include "inputs.h"
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <ctype.h>
|
||||
|
||||
void handle_desktop(void* input_struct, struct tb_event* event)
|
||||
{
|
||||
struct desktop* target = (struct desktop*) input_struct;
|
||||
|
||||
if ((event != NULL) && (event->type == TB_EVENT_KEY))
|
||||
{
|
||||
if (event->key == TB_KEY_ARROW_LEFT || (event->key == TB_KEY_CTRL_H))
|
||||
{
|
||||
input_desktop_right(target);
|
||||
}
|
||||
else if (event->key == TB_KEY_ARROW_RIGHT || (event->key == TB_KEY_CTRL_L))
|
||||
{
|
||||
input_desktop_left(target);
|
||||
}
|
||||
}
|
||||
|
||||
tb_set_cursor(target->x + 2, target->y);
|
||||
}
|
||||
|
||||
void handle_text(void* input_struct, struct tb_event* event)
|
||||
{
|
||||
struct text* target = (struct text*) input_struct;
|
||||
|
||||
if ((event != NULL) && (event->type == TB_EVENT_KEY))
|
||||
{
|
||||
if (event->key == TB_KEY_ARROW_LEFT)
|
||||
{
|
||||
input_text_left(target);
|
||||
}
|
||||
else if (event->key == TB_KEY_ARROW_RIGHT)
|
||||
{
|
||||
input_text_right(target);
|
||||
}
|
||||
else if (event->key == TB_KEY_DELETE)
|
||||
{
|
||||
input_text_delete(target);
|
||||
}
|
||||
else if ((event->key == TB_KEY_BACKSPACE)
|
||||
|| (event->key == TB_KEY_BACKSPACE2))
|
||||
{
|
||||
input_text_backspace(target);
|
||||
}
|
||||
else if (((event->ch > 31) && (event->ch < 127))
|
||||
|| (event->key == TB_KEY_SPACE))
|
||||
{
|
||||
char buf[7] = {0};
|
||||
|
||||
if (event->key == TB_KEY_SPACE)
|
||||
{
|
||||
buf[0] = ' ';
|
||||
}
|
||||
else
|
||||
{
|
||||
utf8_unicode_to_char(buf, event->ch);
|
||||
}
|
||||
|
||||
input_text_write(target, buf[0]);
|
||||
}
|
||||
}
|
||||
|
||||
tb_set_cursor(
|
||||
target->x + (target->cur - target->visible_start),
|
||||
target->y);
|
||||
}
|
||||
|
||||
void input_desktop(struct desktop* target)
|
||||
{
|
||||
target->list = NULL;
|
||||
target->list_simple = NULL;
|
||||
target->cmd = NULL;
|
||||
target->display_server = NULL;
|
||||
target->cur = 0;
|
||||
target->len = 0;
|
||||
|
||||
input_desktop_add(target, strdup(lang.shell), strdup(""), DS_SHELL);
|
||||
input_desktop_add(target, strdup(lang.xinitrc), strdup(config.xinitrc), DS_XINITRC);
|
||||
#if 0
|
||||
input_desktop_add(target, strdup(lang.wayland), strdup(""), DS_WAYLAND);
|
||||
#endif
|
||||
}
|
||||
|
||||
void input_text(struct text* target, uint64_t len)
|
||||
{
|
||||
target->text = malloc(len + 1);
|
||||
|
||||
if (target->text == NULL)
|
||||
{
|
||||
dgn_throw(DGN_ALLOC);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
int ok = mlock(target->text, len + 1);
|
||||
|
||||
if (ok < 0)
|
||||
{
|
||||
dgn_throw(DGN_MLOCK);
|
||||
return;
|
||||
}
|
||||
|
||||
memset(target->text, 0, len + 1);
|
||||
}
|
||||
|
||||
target->cur = target->text;
|
||||
target->end = target->text;
|
||||
target->visible_start = target->text;
|
||||
target->len = len;
|
||||
target->x = 0;
|
||||
target->y = 0;
|
||||
}
|
||||
|
||||
void input_desktop_free(struct desktop* target)
|
||||
{
|
||||
if (target != NULL)
|
||||
{
|
||||
for (uint16_t i = 0; i < target->len; ++i)
|
||||
{
|
||||
if (target->list[i] != NULL)
|
||||
{
|
||||
free(target->list[i]);
|
||||
}
|
||||
|
||||
if (target->cmd[i] != NULL)
|
||||
{
|
||||
free(target->cmd[i]);
|
||||
}
|
||||
}
|
||||
|
||||
free(target->list);
|
||||
free(target->cmd);
|
||||
free(target->display_server);
|
||||
}
|
||||
}
|
||||
|
||||
void input_text_free(struct text* target)
|
||||
{
|
||||
memset(target->text, 0, target->len);
|
||||
munlock(target->text, target->len + 1);
|
||||
free(target->text);
|
||||
}
|
||||
|
||||
void input_desktop_right(struct desktop* target)
|
||||
{
|
||||
++(target->cur);
|
||||
|
||||
if (target->cur >= target->len)
|
||||
{
|
||||
target->cur = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void input_desktop_left(struct desktop* target)
|
||||
{
|
||||
--(target->cur);
|
||||
|
||||
if (target->cur >= target->len)
|
||||
{
|
||||
target->cur = target->len - 1;
|
||||
}
|
||||
}
|
||||
|
||||
void input_desktop_add(
|
||||
struct desktop* target,
|
||||
char* name,
|
||||
char* cmd,
|
||||
enum display_server display_server)
|
||||
{
|
||||
++(target->len);
|
||||
target->list = realloc(target->list, target->len * (sizeof (char*)));
|
||||
target->list_simple = realloc(target->list_simple, target->len * (sizeof (char*)));
|
||||
target->cmd = realloc(target->cmd, target->len * (sizeof (char*)));
|
||||
target->display_server = realloc(
|
||||
target->display_server,
|
||||
target->len * (sizeof (enum display_server)));
|
||||
target->cur = target->len - 1;
|
||||
|
||||
if ((target->list == NULL)
|
||||
|| (target->cmd == NULL)
|
||||
|| (target->display_server == NULL))
|
||||
{
|
||||
dgn_throw(DGN_ALLOC);
|
||||
return;
|
||||
}
|
||||
|
||||
target->list[target->cur] = name;
|
||||
|
||||
int name_len = strlen(name);
|
||||
char* name_simple = strdup(name);
|
||||
|
||||
if (strstr(name_simple, " ") != NULL)
|
||||
{
|
||||
name_simple = strtok(name_simple, " ");
|
||||
}
|
||||
|
||||
for (int i = 0; i < name_len; i++)
|
||||
{
|
||||
name_simple[i] = tolower(name_simple[i]);
|
||||
}
|
||||
|
||||
target->list_simple[target->cur] = name_simple;
|
||||
target->cmd[target->cur] = cmd;
|
||||
target->display_server[target->cur] = display_server;
|
||||
}
|
||||
|
||||
void input_text_right(struct text* target)
|
||||
{
|
||||
if (target->cur < target->end)
|
||||
{
|
||||
++(target->cur);
|
||||
|
||||
if ((target->cur - target->visible_start) > target->visible_len)
|
||||
{
|
||||
++(target->visible_start);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void input_text_left(struct text* target)
|
||||
{
|
||||
if (target->cur > target->text)
|
||||
{
|
||||
--(target->cur);
|
||||
|
||||
if ((target->cur - target->visible_start) < 0)
|
||||
{
|
||||
--(target->visible_start);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void input_text_write(struct text* target, char ascii)
|
||||
{
|
||||
if (ascii <= 0)
|
||||
{
|
||||
return; // unices do not support usernames and passwords other than ascii
|
||||
}
|
||||
|
||||
if ((target->end - target->text + 1) < target->len)
|
||||
{
|
||||
// moves the text to the right to add space for the new ascii char
|
||||
memcpy(target->cur + 1, target->cur, target->end - target->cur);
|
||||
++(target->end);
|
||||
// adds the new char and moves the cursor to the right
|
||||
*(target->cur) = ascii;
|
||||
input_text_right(target);
|
||||
}
|
||||
}
|
||||
|
||||
void input_text_delete(struct text* target)
|
||||
{
|
||||
if (target->cur < target->end)
|
||||
{
|
||||
// moves the text on the right to overwrite the currently pointed char
|
||||
memcpy(target->cur, target->cur + 1, target->end - target->cur + 1);
|
||||
--(target->end);
|
||||
}
|
||||
}
|
||||
|
||||
void input_text_backspace(struct text* target)
|
||||
{
|
||||
if (target->cur > target->text)
|
||||
{
|
||||
input_text_left(target);
|
||||
input_text_delete(target);
|
||||
}
|
||||
}
|
||||
|
||||
void input_text_clear(struct text* target)
|
||||
{
|
||||
memset(target->text, 0, target->len + 1);
|
||||
target->cur = target->text;
|
||||
target->end = target->text;
|
||||
target->visible_start = target->text;
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
#ifndef H_LY_INPUTS
|
||||
#define H_LY_INPUTS
|
||||
|
||||
#include "termbox.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum display_server {DS_WAYLAND, DS_SHELL, DS_XINITRC, DS_XORG};
|
||||
|
||||
struct text
|
||||
{
|
||||
char* text;
|
||||
char* end;
|
||||
int64_t len;
|
||||
char* cur;
|
||||
char* visible_start;
|
||||
uint16_t visible_len;
|
||||
|
||||
uint16_t x;
|
||||
uint16_t y;
|
||||
};
|
||||
|
||||
struct desktop
|
||||
{
|
||||
char** list;
|
||||
char** list_simple;
|
||||
char** cmd;
|
||||
enum display_server* display_server;
|
||||
|
||||
uint16_t cur;
|
||||
uint16_t len;
|
||||
uint16_t visible_len;
|
||||
uint16_t x;
|
||||
uint16_t y;
|
||||
};
|
||||
|
||||
void handle_desktop(void* input_struct, struct tb_event* event);
|
||||
void handle_text(void* input_struct, struct tb_event* event);
|
||||
void input_desktop(struct desktop* target);
|
||||
void input_text(struct text* target, uint64_t len);
|
||||
void input_desktop_free(struct desktop* target);
|
||||
void input_text_free(struct text* target);
|
||||
void input_desktop_right(struct desktop* target);
|
||||
void input_desktop_left(struct desktop* target);
|
||||
void input_desktop_add(
|
||||
struct desktop* target,
|
||||
char* name,
|
||||
char* cmd,
|
||||
enum display_server display_server);
|
||||
void input_text_right(struct text* target);
|
||||
void input_text_left(struct text* target);
|
||||
void input_text_write(struct text* target, char ascii);
|
||||
void input_text_delete(struct text* target);
|
||||
void input_text_backspace(struct text* target);
|
||||
void input_text_clear(struct text* target);
|
||||
|
||||
#endif
|
@ -1,13 +1,125 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
pub const allocator = std.heap.raw_c_allocator;
|
||||
pub const termbox = @cImport({
|
||||
@cInclude("termbox.h");
|
||||
});
|
||||
|
||||
pub fn c_str(str: []const u8) ![:0]u8 {
|
||||
const new_str = try allocator.allocSentinel(u8, str.len, 0);
|
||||
pub const pam = @cImport({
|
||||
@cInclude("security/pam_appl.h");
|
||||
});
|
||||
|
||||
for (str, 0..) |c, i| {
|
||||
new_str[i] = c;
|
||||
pub const c_size = u64;
|
||||
pub const c_uid = u32;
|
||||
pub const c_time = c_long;
|
||||
pub const tm = extern struct {
|
||||
tm_sec: c_int,
|
||||
tm_min: c_int,
|
||||
tm_hour: c_int,
|
||||
tm_mday: c_int,
|
||||
tm_mon: c_int,
|
||||
tm_year: c_int,
|
||||
tm_wday: c_int,
|
||||
tm_yday: c_int,
|
||||
tm_isdst: c_int,
|
||||
};
|
||||
|
||||
pub const _POSIX_HOST_NAME_MAX: c_int = 0xFF;
|
||||
pub const _SC_HOST_NAME_MAX: c_int = 0xB4;
|
||||
|
||||
pub const VT_ACTIVATE: c_int = 0x5606;
|
||||
pub const VT_WAITACTIVE: c_int = 0x5607;
|
||||
|
||||
pub const KDGETLED: c_int = 0x4B31;
|
||||
pub const KDGKBLED: c_int = 0x4B64;
|
||||
|
||||
pub const LED_NUM: c_int = 0x02;
|
||||
pub const LED_CAP: c_int = 0x04;
|
||||
|
||||
pub const K_NUMLOCK: c_int = 0x02;
|
||||
pub const K_CAPSLOCK: c_int = 0x04;
|
||||
|
||||
pub const O_RDONLY: c_uint = 0x00;
|
||||
pub const O_WRONLY: c_uint = 0x01;
|
||||
pub const O_RDWR: c_uint = 0x02;
|
||||
|
||||
pub extern "c" fn fileno(stream: *std.c.FILE) c_int;
|
||||
pub extern "c" fn sysconf(name: c_int) c_long;
|
||||
pub extern "c" fn time(second: ?*c_time) c_time;
|
||||
pub extern "c" fn localtime(timer: *const c_time) *tm;
|
||||
pub extern "c" fn strftime(str: [*:0]u8, maxsize: c_size, format: [*:0]const u8, timeptr: *const tm) c_size;
|
||||
pub extern "c" fn setenv(name: [*:0]const u8, value: [*:0]const u8, overwrite: c_int) c_int;
|
||||
pub extern "c" fn getuid() c_uid;
|
||||
|
||||
pub fn getHostName(allocator: Allocator) !struct {
|
||||
buffer: []u8,
|
||||
slice: []const u8,
|
||||
} {
|
||||
const hostname_sysconf = sysconf(_SC_HOST_NAME_MAX);
|
||||
const hostname_max_length: u64 = if (hostname_sysconf < 0) @intCast(_POSIX_HOST_NAME_MAX) else @intCast(hostname_sysconf);
|
||||
|
||||
const buffer = try allocator.alloc(u8, hostname_max_length);
|
||||
|
||||
const error_code = std.c.gethostname(buffer.ptr, hostname_max_length);
|
||||
if (error_code < 0) return error.CannotGetHostName;
|
||||
|
||||
var hostname_length: u64 = 0;
|
||||
for (buffer, 0..) |char, i| {
|
||||
if (char == 0) {
|
||||
hostname_length = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return .{
|
||||
.buffer = buffer,
|
||||
.slice = buffer[0..hostname_length],
|
||||
};
|
||||
}
|
||||
|
||||
pub fn timeAsString(allocator: Allocator, format: []const u8, max_length: u64) ![:0]u8 {
|
||||
const timer = time(null);
|
||||
const tm_info = localtime(&timer);
|
||||
const buffer = try allocator.allocSentinel(u8, max_length, 0);
|
||||
|
||||
const format_z = try allocator.dupeZ(u8, format);
|
||||
defer allocator.free(format_z);
|
||||
|
||||
if (strftime(buffer, max_length, format_z, tm_info) < 0) return error.CannotGetFormattedTime;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
pub fn getLockState(allocator: Allocator, console_dev: []const u8) !struct {
|
||||
numlock: bool,
|
||||
capslock: bool,
|
||||
} {
|
||||
const console_dev_z = try allocator.dupeZ(u8, console_dev);
|
||||
defer allocator.free(console_dev_z);
|
||||
|
||||
const fd = std.c.open(console_dev_z, O_RDONLY);
|
||||
if (fd < 0) return error.CannotOpenConsoleDev;
|
||||
|
||||
var numlock = false;
|
||||
var capslock = false;
|
||||
|
||||
if (builtin.os.tag.isBSD()) {
|
||||
var led: c_int = undefined;
|
||||
_ = std.c.ioctl(fd, KDGETLED, &led);
|
||||
numlock = (led & LED_NUM) != 0;
|
||||
capslock = (led & LED_CAP) != 0;
|
||||
} else {
|
||||
var led: c_char = undefined;
|
||||
_ = std.c.ioctl(fd, KDGKBLED, &led);
|
||||
numlock = (led & K_NUMLOCK) != 0;
|
||||
capslock = (led & K_CAPSLOCK) != 0;
|
||||
}
|
||||
|
||||
return new_str;
|
||||
_ = std.c.close(fd);
|
||||
|
||||
return .{
|
||||
.numlock = numlock,
|
||||
.capslock = capslock,
|
||||
};
|
||||
}
|
||||
|
@ -1,715 +0,0 @@
|
||||
#include "dragonfail.h"
|
||||
#include "termbox.h"
|
||||
|
||||
#include "inputs.h"
|
||||
#include "draw.h"
|
||||
#include "utils.h"
|
||||
#include "config.h"
|
||||
#include "login.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
#include <security/pam_appl.h>
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <utmp.h>
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
int get_free_display()
|
||||
{
|
||||
char xlock[1024];
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < 200; ++i)
|
||||
{
|
||||
snprintf(xlock, 1024, "/tmp/.X%d-lock", i);
|
||||
|
||||
if (access(xlock, F_OK) == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
void reset_terminal(struct passwd* pwd)
|
||||
{
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == 0)
|
||||
{
|
||||
execl(pwd->pw_shell, pwd->pw_shell, "-c", config.term_reset_cmd, NULL);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
int status;
|
||||
waitpid(pid, &status, 0);
|
||||
}
|
||||
|
||||
int login_conv(
|
||||
int num_msg,
|
||||
const struct pam_message** msg,
|
||||
struct pam_response** resp,
|
||||
void* appdata_ptr)
|
||||
{
|
||||
*resp = calloc(num_msg, sizeof (struct pam_response));
|
||||
|
||||
if (*resp == NULL)
|
||||
{
|
||||
return PAM_BUF_ERR;
|
||||
}
|
||||
|
||||
char* username;
|
||||
char* password;
|
||||
int ok = PAM_SUCCESS;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_msg; ++i)
|
||||
{
|
||||
switch (msg[i]->msg_style)
|
||||
{
|
||||
case PAM_PROMPT_ECHO_ON:
|
||||
{
|
||||
username = ((char**) appdata_ptr)[0];
|
||||
(*resp)[i].resp = strdup(username);
|
||||
break;
|
||||
}
|
||||
case PAM_PROMPT_ECHO_OFF:
|
||||
{
|
||||
password = ((char**) appdata_ptr)[1];
|
||||
(*resp)[i].resp = strdup(password);
|
||||
break;
|
||||
}
|
||||
case PAM_ERROR_MSG:
|
||||
{
|
||||
ok = PAM_CONV_ERR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok != PAM_SUCCESS)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok != PAM_SUCCESS)
|
||||
{
|
||||
for (i = 0; i < num_msg; ++i)
|
||||
{
|
||||
if ((*resp)[i].resp == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
free((*resp)[i].resp);
|
||||
(*resp)[i].resp = NULL;
|
||||
}
|
||||
|
||||
free(*resp);
|
||||
*resp = NULL;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
void pam_diagnose(int error, struct term_buf* buf)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
case PAM_ACCT_EXPIRED:
|
||||
{
|
||||
buf->info_line = lang.err_pam_acct_expired;
|
||||
break;
|
||||
}
|
||||
case PAM_AUTH_ERR:
|
||||
{
|
||||
buf->info_line = lang.err_pam_auth;
|
||||
break;
|
||||
}
|
||||
case PAM_AUTHINFO_UNAVAIL:
|
||||
{
|
||||
buf->info_line = lang.err_pam_authinfo_unavail;
|
||||
break;
|
||||
}
|
||||
case PAM_BUF_ERR:
|
||||
{
|
||||
buf->info_line = lang.err_pam_buf;
|
||||
break;
|
||||
}
|
||||
case PAM_CRED_ERR:
|
||||
{
|
||||
buf->info_line = lang.err_pam_cred_err;
|
||||
break;
|
||||
}
|
||||
case PAM_CRED_EXPIRED:
|
||||
{
|
||||
buf->info_line = lang.err_pam_cred_expired;
|
||||
break;
|
||||
}
|
||||
case PAM_CRED_INSUFFICIENT:
|
||||
{
|
||||
buf->info_line = lang.err_pam_cred_insufficient;
|
||||
break;
|
||||
}
|
||||
case PAM_CRED_UNAVAIL:
|
||||
{
|
||||
buf->info_line = lang.err_pam_cred_unavail;
|
||||
break;
|
||||
}
|
||||
case PAM_MAXTRIES:
|
||||
{
|
||||
buf->info_line = lang.err_pam_maxtries;
|
||||
break;
|
||||
}
|
||||
case PAM_NEW_AUTHTOK_REQD:
|
||||
{
|
||||
buf->info_line = lang.err_pam_authok_reqd;
|
||||
break;
|
||||
}
|
||||
case PAM_PERM_DENIED:
|
||||
{
|
||||
buf->info_line = lang.err_pam_perm_denied;
|
||||
break;
|
||||
}
|
||||
case PAM_SESSION_ERR:
|
||||
{
|
||||
buf->info_line = lang.err_pam_session;
|
||||
break;
|
||||
}
|
||||
case PAM_SYSTEM_ERR:
|
||||
{
|
||||
buf->info_line = lang.err_pam_sys;
|
||||
break;
|
||||
}
|
||||
case PAM_USER_UNKNOWN:
|
||||
{
|
||||
buf->info_line = lang.err_pam_user_unknown;
|
||||
break;
|
||||
}
|
||||
case PAM_ABORT:
|
||||
default:
|
||||
{
|
||||
buf->info_line = lang.err_pam_abort;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dgn_throw(DGN_PAM);
|
||||
}
|
||||
|
||||
void env_init(struct passwd* pwd)
|
||||
{
|
||||
extern char** environ;
|
||||
char* term = getenv("TERM");
|
||||
char* lang = getenv("LANG");
|
||||
// clean env
|
||||
environ[0] = NULL;
|
||||
|
||||
setenv("TERM", term ? term : "linux", 1);
|
||||
setenv("HOME", pwd->pw_dir, 1);
|
||||
setenv("PWD", pwd->pw_dir, 1);
|
||||
setenv("SHELL", pwd->pw_shell, 1);
|
||||
setenv("USER", pwd->pw_name, 1);
|
||||
setenv("LOGNAME", pwd->pw_name, 1);
|
||||
setenv("LANG", lang ? lang : "C", 1);
|
||||
|
||||
// Set PATH if specified in the configuration
|
||||
if (strlen(config.path))
|
||||
{
|
||||
int ok = setenv("PATH", config.path, 1);
|
||||
|
||||
if (ok != 0)
|
||||
{
|
||||
dgn_throw(DGN_PATH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void env_xdg_session(const enum display_server display_server)
|
||||
{
|
||||
switch (display_server)
|
||||
{
|
||||
case DS_WAYLAND:
|
||||
{
|
||||
setenv("XDG_SESSION_TYPE", "wayland", 1);
|
||||
break;
|
||||
}
|
||||
case DS_SHELL:
|
||||
{
|
||||
setenv("XDG_SESSION_TYPE", "tty", 0);
|
||||
break;
|
||||
}
|
||||
case DS_XINITRC:
|
||||
case DS_XORG:
|
||||
{
|
||||
setenv("XDG_SESSION_TYPE", "x11", 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void env_xdg(const char* tty_id, const char* desktop_name)
|
||||
{
|
||||
char user[20];
|
||||
snprintf(user, 20, "/run/user/%d", getuid());
|
||||
setenv("XDG_RUNTIME_DIR", user, 0);
|
||||
setenv("XDG_SESSION_CLASS", "user", 0);
|
||||
setenv("XDG_SESSION_ID", "1", 0);
|
||||
setenv("XDG_SESSION_DESKTOP", desktop_name, 0);
|
||||
setenv("XDG_SEAT", "seat0", 0);
|
||||
setenv("XDG_VTNR", tty_id, 0);
|
||||
}
|
||||
|
||||
void add_utmp_entry(
|
||||
struct utmp *entry,
|
||||
char *username,
|
||||
pid_t display_pid
|
||||
) {
|
||||
entry->ut_type = USER_PROCESS;
|
||||
entry->ut_pid = display_pid;
|
||||
strcpy(entry->ut_line, ttyname(STDIN_FILENO) + strlen("/dev/"));
|
||||
|
||||
/* only correct for ptys named /dev/tty[pqr][0-9a-z] */
|
||||
strcpy(entry->ut_id, ttyname(STDIN_FILENO) + strlen("/dev/tty"));
|
||||
|
||||
time((long int *) &entry->ut_time);
|
||||
|
||||
strncpy(entry->ut_user, username, UT_NAMESIZE);
|
||||
memset(entry->ut_host, 0, UT_HOSTSIZE);
|
||||
entry->ut_addr = 0;
|
||||
setutent();
|
||||
|
||||
pututline(entry);
|
||||
}
|
||||
|
||||
void remove_utmp_entry(struct utmp *entry) {
|
||||
entry->ut_type = DEAD_PROCESS;
|
||||
memset(entry->ut_line, 0, UT_LINESIZE);
|
||||
entry->ut_time = 0;
|
||||
memset(entry->ut_user, 0, UT_NAMESIZE);
|
||||
setutent();
|
||||
pututline(entry);
|
||||
endutent();
|
||||
}
|
||||
|
||||
void xauth(const char* display_name, const char* shell, char* pwd)
|
||||
{
|
||||
const char* xauth_file = "lyxauth";
|
||||
char* xauth_dir = getenv("XDG_RUNTIME_DIR");
|
||||
if ((xauth_dir == NULL) || (*xauth_dir == '\0'))
|
||||
{
|
||||
xauth_dir = getenv("XDG_CONFIG_HOME");
|
||||
struct stat sb;
|
||||
if ((xauth_dir == NULL) || (*xauth_dir == '\0'))
|
||||
{
|
||||
xauth_dir = strdup(pwd);
|
||||
strcat(xauth_dir, "/.config");
|
||||
stat(xauth_dir, &sb);
|
||||
if (S_ISDIR(sb.st_mode))
|
||||
{
|
||||
strcat(xauth_dir, "/ly");
|
||||
}
|
||||
else
|
||||
{
|
||||
xauth_dir = pwd;
|
||||
xauth_file = ".lyxauth";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
strcat(xauth_dir, "/ly");
|
||||
}
|
||||
|
||||
// If .config/ly/ or XDG_CONFIG_HOME/ly/ doesn't exist and can't create the directory, use pwd
|
||||
// Passing pwd beforehand is safe since stat will always evaluate false
|
||||
stat(xauth_dir, &sb);
|
||||
if (!S_ISDIR(sb.st_mode) && mkdir(xauth_dir, 0777) == -1)
|
||||
{
|
||||
xauth_dir = pwd;
|
||||
xauth_file = ".lyxauth";
|
||||
}
|
||||
}
|
||||
|
||||
// trim trailing slashes
|
||||
int i = strlen(xauth_dir) - 1;
|
||||
while (xauth_dir[i] == '/') i--;
|
||||
xauth_dir[i + 1] = '\0';
|
||||
|
||||
char xauthority[256];
|
||||
snprintf(xauthority, 256, "%s/%s", xauth_dir, xauth_file);
|
||||
setenv("XAUTHORITY", xauthority, 1);
|
||||
setenv("DISPLAY", display_name, 1);
|
||||
|
||||
FILE* fp = fopen(xauthority, "ab+");
|
||||
|
||||
if (fp != NULL)
|
||||
{
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == 0)
|
||||
{
|
||||
char cmd[1024];
|
||||
snprintf(
|
||||
cmd,
|
||||
1024,
|
||||
"%s add %s . `%s`",
|
||||
config.xauth_cmd,
|
||||
display_name,
|
||||
config.mcookie_cmd);
|
||||
execl(shell, shell, "-c", cmd, NULL);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
int status;
|
||||
waitpid(pid, &status, 0);
|
||||
}
|
||||
|
||||
void xorg(
|
||||
struct passwd* pwd,
|
||||
const char* vt,
|
||||
const char* desktop_cmd)
|
||||
{
|
||||
char display_name[4];
|
||||
|
||||
snprintf(display_name, 3, ":%d", get_free_display());
|
||||
xauth(display_name, pwd->pw_shell, pwd->pw_dir);
|
||||
|
||||
// start xorg
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == 0)
|
||||
{
|
||||
char x_cmd[1024];
|
||||
snprintf(
|
||||
x_cmd,
|
||||
1024,
|
||||
"%s %s %s",
|
||||
config.x_cmd,
|
||||
display_name,
|
||||
vt);
|
||||
execl(pwd->pw_shell, pwd->pw_shell, "-c", x_cmd, NULL);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
int ok;
|
||||
xcb_connection_t* xcb;
|
||||
|
||||
do
|
||||
{
|
||||
xcb = xcb_connect(NULL, NULL);
|
||||
ok = xcb_connection_has_error(xcb);
|
||||
kill(pid, 0);
|
||||
}
|
||||
while((ok != 0) && (errno != ESRCH));
|
||||
|
||||
if (ok != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pid_t xorg_pid = fork();
|
||||
|
||||
if (xorg_pid == 0)
|
||||
{
|
||||
char de_cmd[1024];
|
||||
snprintf(
|
||||
de_cmd,
|
||||
1024,
|
||||
"%s %s",
|
||||
config.x_cmd_setup,
|
||||
desktop_cmd);
|
||||
execl(pwd->pw_shell, pwd->pw_shell, "-c", de_cmd, NULL);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
int status;
|
||||
waitpid(xorg_pid, &status, 0);
|
||||
xcb_disconnect(xcb);
|
||||
kill(pid, 0);
|
||||
|
||||
if (errno != ESRCH)
|
||||
{
|
||||
kill(pid, SIGTERM);
|
||||
waitpid(pid, &status, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void wayland(
|
||||
struct passwd* pwd,
|
||||
const char* desktop_cmd)
|
||||
{
|
||||
|
||||
char cmd[1024];
|
||||
snprintf(cmd, 1024, "%s %s", config.wayland_cmd, desktop_cmd);
|
||||
execl(pwd->pw_shell, pwd->pw_shell, "-c", cmd, NULL);
|
||||
}
|
||||
|
||||
void shell(struct passwd* pwd)
|
||||
{
|
||||
const char* pos = strrchr(pwd->pw_shell, '/');
|
||||
char args[1024];
|
||||
args[0] = '-';
|
||||
|
||||
if (pos != NULL)
|
||||
{
|
||||
pos = pos + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
pos = pwd->pw_shell;
|
||||
}
|
||||
|
||||
strncpy(args + 1, pos, 1023);
|
||||
execl(pwd->pw_shell, args, NULL);
|
||||
}
|
||||
|
||||
// pam_do performs the pam action specified in pam_action
|
||||
// on pam_action fail, call diagnose and end pam session
|
||||
int pam_do(
|
||||
int (pam_action)(struct pam_handle *, int),
|
||||
struct pam_handle *handle,
|
||||
int flags,
|
||||
struct term_buf *buf)
|
||||
{
|
||||
int status = pam_action(handle, flags);
|
||||
|
||||
if (status != PAM_SUCCESS) {
|
||||
pam_diagnose(status, buf);
|
||||
pam_end(handle, status);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void auth(
|
||||
struct desktop* desktop,
|
||||
struct text* login,
|
||||
struct text* password,
|
||||
struct term_buf* buf)
|
||||
{
|
||||
int ok;
|
||||
|
||||
char tty_id [3];
|
||||
snprintf(tty_id, 3, "%d", config.tty);
|
||||
|
||||
// Add XDG environment variables
|
||||
env_xdg_session(desktop->display_server[desktop->cur]);
|
||||
env_xdg(tty_id, desktop->list_simple[desktop->cur]);
|
||||
|
||||
// open pam session
|
||||
const char* creds[2] = {login->text, password->text};
|
||||
struct pam_conv conv = {login_conv, creds};
|
||||
struct pam_handle* handle;
|
||||
|
||||
ok = pam_start(config.service_name, NULL, &conv, &handle);
|
||||
|
||||
if (ok != PAM_SUCCESS)
|
||||
{
|
||||
pam_diagnose(ok, buf);
|
||||
pam_end(handle, ok);
|
||||
return;
|
||||
}
|
||||
|
||||
ok = pam_do(pam_authenticate, handle, 0, buf);
|
||||
|
||||
if (ok != PAM_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ok = pam_do(pam_acct_mgmt, handle, 0, buf);
|
||||
|
||||
if (ok != PAM_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ok = pam_do(pam_setcred, handle, PAM_ESTABLISH_CRED, buf);
|
||||
|
||||
if (ok != PAM_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ok = pam_do(pam_open_session, handle, 0, buf);
|
||||
|
||||
if (ok != PAM_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// clear the credentials
|
||||
input_text_clear(password);
|
||||
|
||||
// get passwd structure
|
||||
struct passwd* pwd = getpwnam(login->text);
|
||||
endpwent();
|
||||
|
||||
if (pwd == NULL)
|
||||
{
|
||||
dgn_throw(DGN_PWNAM);
|
||||
pam_end(handle, ok);
|
||||
return;
|
||||
}
|
||||
|
||||
// set user shell
|
||||
if (pwd->pw_shell[0] == '\0')
|
||||
{
|
||||
setusershell();
|
||||
|
||||
char* shell = getusershell();
|
||||
|
||||
if (shell != NULL)
|
||||
{
|
||||
strcpy(pwd->pw_shell, shell);
|
||||
}
|
||||
|
||||
endusershell();
|
||||
}
|
||||
|
||||
// restore regular terminal mode
|
||||
tb_clear();
|
||||
tb_present();
|
||||
tb_shutdown();
|
||||
|
||||
// start desktop environment
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == 0)
|
||||
{
|
||||
// set user info
|
||||
ok = initgroups(pwd->pw_name, pwd->pw_gid);
|
||||
|
||||
if (ok != 0)
|
||||
{
|
||||
dgn_throw(DGN_USER_INIT);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ok = setgid(pwd->pw_gid);
|
||||
|
||||
if (ok != 0)
|
||||
{
|
||||
dgn_throw(DGN_USER_GID);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ok = setuid(pwd->pw_uid);
|
||||
|
||||
if (ok != 0)
|
||||
{
|
||||
dgn_throw(DGN_USER_UID);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// get a display
|
||||
char vt[5];
|
||||
snprintf(vt, 5, "vt%d", config.tty);
|
||||
|
||||
// set env (this clears the environment)
|
||||
env_init(pwd);
|
||||
// Re-add XDG environment variables from lines 508,509
|
||||
env_xdg_session(desktop->display_server[desktop->cur]);
|
||||
env_xdg(tty_id, desktop->list_simple[desktop->cur]);
|
||||
|
||||
if (dgn_catch())
|
||||
{
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// add pam variables
|
||||
char** env = pam_getenvlist(handle);
|
||||
|
||||
for (uint16_t i = 0; env && env[i]; ++i)
|
||||
{
|
||||
putenv(env[i]);
|
||||
}
|
||||
|
||||
// execute
|
||||
int ok = chdir(pwd->pw_dir);
|
||||
|
||||
if (ok != 0)
|
||||
{
|
||||
dgn_throw(DGN_CHDIR);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
reset_terminal(pwd);
|
||||
switch (desktop->display_server[desktop->cur])
|
||||
{
|
||||
case DS_WAYLAND:
|
||||
{
|
||||
wayland(pwd, desktop->cmd[desktop->cur]);
|
||||
break;
|
||||
}
|
||||
case DS_SHELL:
|
||||
{
|
||||
shell(pwd);
|
||||
break;
|
||||
}
|
||||
case DS_XINITRC:
|
||||
case DS_XORG:
|
||||
{
|
||||
xorg(pwd, vt, desktop->cmd[desktop->cur]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
// add utmp audit
|
||||
struct utmp entry;
|
||||
add_utmp_entry(&entry, pwd->pw_name, pid);
|
||||
|
||||
// wait for the session to stop
|
||||
int status;
|
||||
waitpid(pid, &status, 0);
|
||||
remove_utmp_entry(&entry);
|
||||
|
||||
reset_terminal(pwd);
|
||||
|
||||
// reinit termbox
|
||||
tb_init();
|
||||
tb_select_output_mode(TB_OUTPUT_NORMAL);
|
||||
|
||||
// reload the desktop environment list on logout
|
||||
input_desktop_free(desktop);
|
||||
input_desktop(desktop);
|
||||
desktop_load(desktop);
|
||||
|
||||
// close pam session
|
||||
ok = pam_do(pam_close_session, handle, 0, buf);
|
||||
|
||||
if (ok != PAM_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ok = pam_do(pam_setcred, handle, PAM_DELETE_CRED, buf);
|
||||
|
||||
if (ok != PAM_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ok = pam_end(handle, 0);
|
||||
|
||||
if (ok != PAM_SUCCESS)
|
||||
{
|
||||
pam_diagnose(ok, buf);
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +0,0 @@
|
||||
#ifndef H_LY_LOGIN
|
||||
#define H_LY_LOGIN
|
||||
|
||||
#include "draw.h"
|
||||
#include "inputs.h"
|
||||
|
||||
void auth(
|
||||
struct desktop* desktop,
|
||||
struct text* login,
|
||||
struct text* password,
|
||||
struct term_buf* buf);
|
||||
|
||||
#endif
|
@ -1,383 +1,507 @@
|
||||
const std = @import("std");
|
||||
const config = @import("config.zig");
|
||||
const utils = @import("utils.zig");
|
||||
const build_options = @import("build_options");
|
||||
const builtin = @import("builtin");
|
||||
const clap = @import("clap");
|
||||
const auth = @import("auth.zig");
|
||||
const bigclock = @import("bigclock.zig");
|
||||
const interop = @import("interop.zig");
|
||||
const Doom = @import("animations/Doom.zig");
|
||||
const Matrix = @import("animations/Matrix.zig");
|
||||
const TerminalBuffer = @import("tui/TerminalBuffer.zig");
|
||||
const Desktop = @import("tui/components/Desktop.zig");
|
||||
const Text = @import("tui/components/Text.zig");
|
||||
const Config = @import("config/Config.zig");
|
||||
const ConfigReader = @import("config/ConfigReader.zig");
|
||||
const Lang = @import("config/Lang.zig");
|
||||
|
||||
const termbox = interop.termbox;
|
||||
|
||||
const LY_VERSION = "1.0.0";
|
||||
|
||||
pub const c = @cImport({
|
||||
@cInclude("dragonfail.h");
|
||||
@cInclude("termbox.h");
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = gpa.deinit();
|
||||
|
||||
const allocator = gpa.allocator();
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
|
||||
// Load arguments
|
||||
const params = comptime clap.parseParamsComptime(
|
||||
\\-h, --help Shows all commands.
|
||||
\\-v, --version Shows the version of Ly.
|
||||
\\-c, --config <str> Overrides the default configuration path. Example: --config /usr/share/ly
|
||||
);
|
||||
|
||||
var diag = clap.Diagnostic{};
|
||||
var res = clap.parse(clap.Help, ¶ms, clap.parsers.default, .{ .diagnostic = &diag }) catch |err| {
|
||||
diag.report(stderr, err) catch {};
|
||||
return err;
|
||||
};
|
||||
defer res.deinit();
|
||||
|
||||
var config: Config = undefined;
|
||||
var lang: Lang = undefined;
|
||||
var info_line: []const u8 = undefined;
|
||||
|
||||
if (res.args.help != 0) {
|
||||
try clap.help(stderr, clap.Help, ¶ms, .{});
|
||||
|
||||
_ = try stderr.write("Note: if you want to configure Ly, please check the config file, which is usually located at /etc/ly/config.ini.\n");
|
||||
std.os.exit(0);
|
||||
}
|
||||
if (res.args.version != 0) {
|
||||
_ = try stderr.write("Ly version " ++ LY_VERSION ++ "\n");
|
||||
std.os.exit(0);
|
||||
}
|
||||
|
||||
@cInclude("draw.h");
|
||||
@cInclude("inputs.h");
|
||||
@cInclude("login.h");
|
||||
@cInclude("utils.h");
|
||||
@cInclude("config.h");
|
||||
});
|
||||
// Load configuration file
|
||||
var config_reader = ConfigReader.init(allocator);
|
||||
defer config_reader.deinit();
|
||||
|
||||
// Compile-time settings
|
||||
const LY_VERSION = "0.7.0";
|
||||
const MAX_AUTH_FAILS = 10;
|
||||
if (res.args.config) |s| {
|
||||
const trailing_slash = if (s[s.len - 1] != '/') "/" else "";
|
||||
|
||||
// Main allocator for Ly
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
const config_path = try std.fmt.allocPrint(allocator, "{s}{s}config.ini", .{ s, trailing_slash });
|
||||
defer allocator.free(config_path);
|
||||
|
||||
pub const allocator = gpa.allocator();
|
||||
config = try config_reader.readConfig(config_path);
|
||||
|
||||
// Ly general and language configuration
|
||||
pub var c_config: c.struct_config = undefined;
|
||||
pub var c_lang: c.struct_lang = undefined;
|
||||
const lang_path = try std.fmt.allocPrint(allocator, "{s}{s}lang/{s}.ini", .{ s, trailing_slash, config.ly.lang });
|
||||
defer allocator.free(lang_path);
|
||||
|
||||
comptime {
|
||||
@export(c_config, .{ .name = "config" });
|
||||
@export(c_lang, .{ .name = "lang" });
|
||||
}
|
||||
lang = try config_reader.readLang(lang_path);
|
||||
} else {
|
||||
config = try config_reader.readConfig(build_options.data_directory ++ "/config.ini");
|
||||
|
||||
// Main function
|
||||
pub fn main() !void {
|
||||
// Initialize structs
|
||||
var config_ptr = try allocator.create(c.struct_config);
|
||||
defer allocator.destroy(config_ptr);
|
||||
|
||||
var lang_ptr = try allocator.create(c.struct_lang);
|
||||
defer allocator.destroy(lang_ptr);
|
||||
|
||||
c_config = config_ptr.*;
|
||||
c_lang = lang_ptr.*;
|
||||
|
||||
// Initialize error library
|
||||
log_init(c.dgn_init());
|
||||
|
||||
// Parse command line arguments
|
||||
var config_path: []const u8 = "";
|
||||
|
||||
var process_args = try std.process.argsAlloc(allocator);
|
||||
defer std.process.argsFree(allocator, process_args);
|
||||
|
||||
if (process_args.len > 1) {
|
||||
var first_arg = process_args[1];
|
||||
|
||||
if (std.mem.eql(u8, first_arg, "--help") or std.mem.eql(u8, first_arg, "-h")) {
|
||||
std.debug.print("--help (-h) | Shows this help message.\n", .{});
|
||||
std.debug.print("--version (-v) | Shows the version of Ly.\n", .{});
|
||||
std.debug.print("--config (-c) <path> | Overrides the configuration file path.\n", .{});
|
||||
std.debug.print("\n", .{});
|
||||
std.debug.print("If you want to configure Ly, please check the config file, usually located at /etc/ly/config.ini.\n", .{});
|
||||
std.os.exit(0);
|
||||
} else if (std.mem.eql(u8, first_arg, "--version") or std.mem.eql(u8, first_arg, "-v")) {
|
||||
std.debug.print("Ly version {s}.\n", .{LY_VERSION});
|
||||
std.os.exit(0);
|
||||
} else if (std.mem.eql(u8, first_arg, "--config") or std.mem.eql(u8, first_arg, "-c")) {
|
||||
if (process_args.len != 3) {
|
||||
std.debug.print("Invalid usage! Correct usage: 'ly --config <path>'.\n", .{});
|
||||
std.os.exit(1);
|
||||
}
|
||||
const lang_path = try std.fmt.allocPrint(allocator, "{s}/lang/{s}.ini", .{ build_options.data_directory, config.ly.lang });
|
||||
defer allocator.free(lang_path);
|
||||
|
||||
config_path = process_args[2];
|
||||
} else {
|
||||
std.debug.print("Invalid argument: '{s}'.\n", .{first_arg});
|
||||
std.os.exit(1);
|
||||
}
|
||||
lang = try config_reader.readLang(lang_path);
|
||||
}
|
||||
|
||||
// Load configuration and language
|
||||
try config.config_load(config_path);
|
||||
try config.lang_load();
|
||||
// Initialize information line with host name
|
||||
get_host_name: {
|
||||
const host_name_struct = interop.getHostName(allocator) catch |err| {
|
||||
if (err == error.CannotGetHostName) {
|
||||
info_line = lang.ly.err_hostname;
|
||||
} else {
|
||||
info_line = lang.ly.err_alloc;
|
||||
}
|
||||
break :get_host_name;
|
||||
};
|
||||
defer allocator.free(host_name_struct.buffer);
|
||||
|
||||
if (c.dgn_catch() != 0) {
|
||||
config.config_free();
|
||||
config.lang_free();
|
||||
std.os.exit(1);
|
||||
info_line = host_name_struct.slice;
|
||||
}
|
||||
|
||||
// Initialize inputs
|
||||
var desktop = try allocator.create(c.struct_desktop);
|
||||
defer allocator.destroy(desktop);
|
||||
// Initialize termbox
|
||||
_ = termbox.tb_init();
|
||||
defer termbox.tb_shutdown();
|
||||
|
||||
var username = try allocator.create(c.struct_text);
|
||||
defer allocator.destroy(username);
|
||||
_ = termbox.tb_select_output_mode(termbox.TB_OUTPUT_NORMAL);
|
||||
termbox.tb_clear();
|
||||
|
||||
var password = try allocator.create(c.struct_text);
|
||||
defer allocator.destroy(password);
|
||||
// Initialize terminal buffer
|
||||
const labels_max_length = @max(lang.ly.login.len, lang.ly.password.len);
|
||||
|
||||
c.input_desktop(desktop);
|
||||
c.input_text(username, config.ly_config.ly.max_login_len);
|
||||
c.input_text(password, config.ly_config.ly.max_password_len);
|
||||
var buffer = TerminalBuffer.init(config.ly.margin_box_v, config.ly.margin_box_h, config.ly.input_len, labels_max_length, config.ly.fg, config.ly.bg);
|
||||
|
||||
utils.desktop_load(desktop);
|
||||
try utils.load(desktop, username);
|
||||
// Initialize components
|
||||
var desktop = try Desktop.init(allocator, &buffer, config.ly.max_desktop_len);
|
||||
defer desktop.deinit();
|
||||
|
||||
// Start termbox
|
||||
_ = c.tb_init();
|
||||
_ = c.tb_select_output_mode(c.TB_OUTPUT_NORMAL);
|
||||
c.tb_clear();
|
||||
desktop.addEnvironment(lang.ly.shell, "", .shell) catch {
|
||||
info_line = lang.ly.err_alloc;
|
||||
};
|
||||
desktop.addEnvironment(lang.ly.xinitrc, config.ly.xinitrc, .xinitrc) catch {
|
||||
info_line = lang.ly.err_alloc;
|
||||
};
|
||||
|
||||
// Initialize visible elements
|
||||
var event = try allocator.create(c.struct_tb_event);
|
||||
defer allocator.destroy(event);
|
||||
try desktop.crawl(config.ly.waylandsessions, .wayland);
|
||||
try desktop.crawl(config.ly.xsessions, .x11);
|
||||
|
||||
var buffer = try allocator.create(c.struct_term_buf);
|
||||
defer allocator.destroy(buffer);
|
||||
var login = try Text.init(allocator, &buffer, config.ly.max_login_len);
|
||||
defer login.deinit();
|
||||
|
||||
// Place the cursor on the login field if there is no saved username
|
||||
// If there is, place the curser on the password field
|
||||
var active_input: config.Inputs = undefined;
|
||||
if (config.ly_config.ly.default_input == .login and username.text != username.end) {
|
||||
active_input = .password;
|
||||
} else {
|
||||
active_input = config.ly_config.ly.default_input;
|
||||
var password = try Text.init(allocator, &buffer, config.ly.max_password_len);
|
||||
defer password.deinit();
|
||||
|
||||
// Load last saved username and desktop selection, if any
|
||||
if (config.ly.load) load_last_saved: {
|
||||
var file = std.fs.openFileAbsolute(config.ly.save_file, .{}) catch break :load_last_saved;
|
||||
defer file.close();
|
||||
|
||||
const reader = file.reader();
|
||||
const username_length = try reader.readIntLittle(u64);
|
||||
|
||||
const username_buffer = try file.readToEndAlloc(allocator, username_length);
|
||||
defer allocator.free(username_buffer);
|
||||
|
||||
_ = try reader.read(username_buffer);
|
||||
|
||||
const current_desktop = try reader.readIntLittle(u64);
|
||||
|
||||
const load_buffer = try file.readToEndAlloc(allocator, config.ly.max_login_len + 5);
|
||||
defer allocator.free(load_buffer);
|
||||
|
||||
if (username_buffer.len > 0) {
|
||||
try login.text.appendSlice(username_buffer);
|
||||
login.end = username_buffer.len;
|
||||
}
|
||||
|
||||
if (current_desktop < desktop.environments.items.len) desktop.current = current_desktop;
|
||||
}
|
||||
|
||||
// Initialize drawing code
|
||||
c.draw_init(buffer);
|
||||
|
||||
// draw_box() and position_input() are called because they need to be
|
||||
// called before the switch case for the cursor to be positioned correctly
|
||||
c.draw_box(buffer);
|
||||
c.position_input(buffer, desktop, username, password);
|
||||
|
||||
switch (active_input) {
|
||||
.session => {
|
||||
c.handle_desktop(desktop, event);
|
||||
},
|
||||
.login => {
|
||||
c.handle_text(username, event);
|
||||
},
|
||||
.password => {
|
||||
c.handle_text(password, event);
|
||||
},
|
||||
var active_input = if (config.ly.default_input == .login and login.text.items.len != login.end) .password else config.ly.default_input;
|
||||
|
||||
// Place components on the screen
|
||||
{
|
||||
buffer.drawBoxCenter(!config.ly.hide_borders, config.ly.blank_box);
|
||||
|
||||
const coordinates = buffer.calculateComponentCoordinates();
|
||||
desktop.position(coordinates.x, coordinates.y + 2, coordinates.visible_length);
|
||||
login.position(coordinates.x, coordinates.y + 4, coordinates.visible_length);
|
||||
password.position(coordinates.x, coordinates.y + 6, coordinates.visible_length);
|
||||
|
||||
switch (active_input) {
|
||||
.session => desktop.handle(null),
|
||||
.login => login.handle(null) catch {
|
||||
info_line = lang.ly.err_alloc;
|
||||
},
|
||||
.password => password.handle(null) catch {
|
||||
info_line = lang.ly.err_alloc;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if (config.ly_config.ly.animate) {
|
||||
c.animate_init(buffer);
|
||||
// Initialize the animation, if any
|
||||
var doom: Doom = undefined;
|
||||
var matrix: Matrix = undefined;
|
||||
|
||||
if (c.dgn_catch() != 0) {
|
||||
config.ly_config.ly.animate = false;
|
||||
c.dgn_reset();
|
||||
switch (config.ly.animation) {
|
||||
.none => {},
|
||||
.doom => doom = try Doom.init(allocator, &buffer),
|
||||
.matrix => matrix = try Matrix.init(allocator, &buffer),
|
||||
}
|
||||
defer {
|
||||
switch (config.ly.animation) {
|
||||
.none => {},
|
||||
.doom => doom.deinit(),
|
||||
.matrix => matrix.deinit(),
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize state information
|
||||
var err: c_int = 0;
|
||||
const animate = config.ly.animation != .none;
|
||||
const has_clock = config.ly.clock.len > 0;
|
||||
const shutdown_key = try std.fmt.parseInt(u8, config.ly.shutdown_key[1..], 10);
|
||||
const restart_key = try std.fmt.parseInt(u8, config.ly.restart_key[1..], 10);
|
||||
|
||||
var event = std.mem.zeroes(termbox.tb_event);
|
||||
var run = true;
|
||||
var update = true;
|
||||
var reboot = false;
|
||||
var resolution_changed = false;
|
||||
var shutdown = false;
|
||||
var auth_fails: u8 = 0;
|
||||
var restart = false;
|
||||
var auth_fails: u64 = 0;
|
||||
|
||||
// Switch to selected TTY if possible
|
||||
open_console_dev: {
|
||||
const console_dev_z = allocator.dupeZ(u8, config.ly.console_dev) catch {
|
||||
info_line = lang.ly.err_alloc;
|
||||
break :open_console_dev;
|
||||
};
|
||||
defer allocator.free(console_dev_z);
|
||||
|
||||
const fd = std.c.open(console_dev_z, interop.O_WRONLY);
|
||||
defer _ = std.c.close(fd);
|
||||
|
||||
if (fd < 0) {
|
||||
info_line = lang.ly.err_console_dev;
|
||||
break :open_console_dev;
|
||||
}
|
||||
|
||||
c.switch_tty(buffer);
|
||||
_ = std.c.ioctl(fd, interop.VT_ACTIVATE, config.ly.tty);
|
||||
_ = std.c.ioctl(fd, interop.VT_WAITACTIVE, config.ly.tty);
|
||||
}
|
||||
|
||||
// Main loop
|
||||
while (run) {
|
||||
// If there's no input or there's an animation, a resolution change needs to be checked
|
||||
if (!update or config.ly.animation != .none) {
|
||||
if (!update) std.time.sleep(100_000_000);
|
||||
|
||||
termbox.tb_present(); // Required to update tb_width(), tb_height() and tb_cell_buffer()
|
||||
|
||||
const width: u64 = @intCast(termbox.tb_width());
|
||||
const height: u64 = @intCast(termbox.tb_height());
|
||||
|
||||
if (width != buffer.width) {
|
||||
buffer.width = width;
|
||||
resolution_changed = true;
|
||||
}
|
||||
|
||||
if (height != buffer.height) {
|
||||
buffer.height = height;
|
||||
resolution_changed = true;
|
||||
}
|
||||
|
||||
// If it did change, then update the cell buffer, reallocate the current animation's buffers, and force a draw update
|
||||
if (resolution_changed) {
|
||||
buffer.buffer = termbox.tb_cell_buffer();
|
||||
|
||||
switch (config.ly.animation) {
|
||||
.none => {},
|
||||
.doom => doom.realloc() catch {
|
||||
info_line = lang.ly.err_alloc;
|
||||
},
|
||||
.matrix => matrix.realloc() catch {
|
||||
info_line = lang.ly.err_alloc;
|
||||
},
|
||||
}
|
||||
|
||||
update = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (update) {
|
||||
if (auth_fails < MAX_AUTH_FAILS) {
|
||||
// If the user entered a wrong password 10 times in a row, play a cascade animation, else update normally
|
||||
if (auth_fails < 10) {
|
||||
switch (active_input) {
|
||||
.session => {
|
||||
c.handle_desktop(desktop, event);
|
||||
},
|
||||
.login => {
|
||||
c.handle_text(username, event);
|
||||
.session => desktop.handle(null),
|
||||
.login => login.handle(null) catch {
|
||||
info_line = lang.ly.err_alloc;
|
||||
},
|
||||
.password => {
|
||||
c.handle_text(password, event);
|
||||
.password => password.handle(null) catch {
|
||||
info_line = lang.ly.err_alloc;
|
||||
},
|
||||
}
|
||||
|
||||
c.tb_clear();
|
||||
c.animate(buffer);
|
||||
c.draw_bigclock(buffer);
|
||||
c.draw_box(buffer);
|
||||
c.draw_clock(buffer);
|
||||
c.draw_labels(buffer);
|
||||
if (!config.ly_config.ly.hide_f1_commands) {
|
||||
c.draw_f_commands();
|
||||
termbox.tb_clear();
|
||||
|
||||
switch (config.ly.animation) {
|
||||
.none => {},
|
||||
.doom => doom.draw(),
|
||||
.matrix => matrix.draw(),
|
||||
}
|
||||
c.draw_lock_state(buffer);
|
||||
c.position_input(buffer, desktop, username, password);
|
||||
c.draw_desktop(desktop);
|
||||
c.draw_input(username);
|
||||
c.draw_input_mask(password);
|
||||
update = config.ly_config.ly.animate;
|
||||
} else {
|
||||
std.time.sleep(10000000); // Sleep 0.01 seconds
|
||||
update = c.cascade(buffer, &auth_fails);
|
||||
}
|
||||
|
||||
c.tb_present();
|
||||
}
|
||||
if (config.ly.bigclock and buffer.box_height + (bigclock.HEIGHT + 2) * 2 < buffer.height) draw_big_clock: {
|
||||
const format = "%H:%M";
|
||||
const xo = buffer.width / 2 - (format.len * (bigclock.WIDTH + 1)) / 2;
|
||||
const yo = (buffer.height - buffer.box_height) / 2 - bigclock.HEIGHT - 2;
|
||||
|
||||
var timeout: c_int = -1;
|
||||
const clock_str = interop.timeAsString(allocator, format, format.len + 1) catch {
|
||||
info_line = lang.ly.err_alloc;
|
||||
break :draw_big_clock;
|
||||
};
|
||||
defer allocator.free(clock_str);
|
||||
|
||||
if (config.ly_config.ly.animate) {
|
||||
timeout = config.ly_config.ly.min_refresh_delta;
|
||||
} else {
|
||||
// TODO: Use the Zig standard library directly
|
||||
var time = try allocator.create(std.os.linux.timeval);
|
||||
defer allocator.destroy(time);
|
||||
for (0..format.len) |i| {
|
||||
const clock_cell = bigclock.clockCell(animate, clock_str[i], buffer.fg, buffer.bg);
|
||||
bigclock.alphaBlit(buffer.buffer, xo + i * (bigclock.WIDTH + 1), yo, buffer.width, buffer.height, clock_cell);
|
||||
}
|
||||
}
|
||||
|
||||
_ = std.os.linux.gettimeofday(time, undefined);
|
||||
buffer.drawBoxCenter(!config.ly.hide_borders, config.ly.blank_box);
|
||||
|
||||
if (config.ly_config.ly.bigclock) {
|
||||
timeout = @intCast((60 - @mod(time.tv_sec, 60)) * 1000 - @divTrunc(time.tv_usec, 1000) + 1);
|
||||
} else if (config.ly_config.ly.clock.len > 0) {
|
||||
timeout = @intCast(1000 - @divTrunc(time.tv_usec, 1000) + 1);
|
||||
}
|
||||
}
|
||||
if (has_clock) draw_clock: {
|
||||
const clock_buffer = interop.timeAsString(allocator, config.ly.clock, 32) catch {
|
||||
info_line = lang.ly.err_alloc;
|
||||
break :draw_clock;
|
||||
};
|
||||
defer allocator.free(clock_buffer);
|
||||
|
||||
if (timeout == -1) {
|
||||
err = c.tb_poll_event(event);
|
||||
} else {
|
||||
err = c.tb_peek_event(event, timeout);
|
||||
}
|
||||
var clock_str_length: u64 = 0;
|
||||
for (clock_buffer, 0..) |char, i| {
|
||||
if (char == 0) {
|
||||
clock_str_length = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (err < 0) {
|
||||
continue;
|
||||
}
|
||||
if (clock_str_length == 0) return error.FormattedTimeEmpty;
|
||||
|
||||
if (event.type == c.TB_EVENT_KEY) {
|
||||
switch (event.key) {
|
||||
c.TB_KEY_F1 => {
|
||||
shutdown = true;
|
||||
run = false;
|
||||
},
|
||||
c.TB_KEY_F2 => {
|
||||
reboot = true;
|
||||
run = false;
|
||||
},
|
||||
c.TB_KEY_CTRL_C => {
|
||||
run = false;
|
||||
},
|
||||
c.TB_KEY_CTRL_U => {
|
||||
if (active_input != .session) {
|
||||
switch (active_input) {
|
||||
.login => {
|
||||
c.input_text_clear(username);
|
||||
},
|
||||
.password => {
|
||||
c.input_text_clear(password);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
buffer.drawLabel(clock_buffer[0..clock_str_length], buffer.width - clock_str_length, 0);
|
||||
}
|
||||
|
||||
update = true;
|
||||
}
|
||||
},
|
||||
c.TB_KEY_CTRL_K, c.TB_KEY_ARROW_UP => {
|
||||
if (active_input != .session) {
|
||||
active_input = switch (active_input) {
|
||||
.login => .session,
|
||||
.password => .login,
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
update = true;
|
||||
}
|
||||
},
|
||||
c.TB_KEY_CTRL_J, c.TB_KEY_ARROW_DOWN => {
|
||||
if (active_input != .password) {
|
||||
active_input = switch (active_input) {
|
||||
.session => .login,
|
||||
.login => .password,
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
update = true;
|
||||
}
|
||||
},
|
||||
c.TB_KEY_TAB => {
|
||||
active_input = switch (active_input) {
|
||||
.session => .login,
|
||||
.login => .password,
|
||||
.password => .session,
|
||||
};
|
||||
const label_x = buffer.box_x + buffer.margin_box_h;
|
||||
const label_y = buffer.box_y + buffer.margin_box_v;
|
||||
|
||||
update = true;
|
||||
},
|
||||
c.TB_KEY_ENTER => {
|
||||
try utils.save(desktop, username);
|
||||
c.auth(desktop, username, password, buffer);
|
||||
buffer.drawLabel(lang.ly.login, label_x, label_y + 4);
|
||||
buffer.drawLabel(lang.ly.password, label_x, label_y + 6);
|
||||
|
||||
if (c.dgn_catch() != 0) {
|
||||
auth_fails += 1;
|
||||
if (info_line.len > 0) {
|
||||
const x = buffer.box_x + ((buffer.box_width - info_line.len) / 2);
|
||||
buffer.drawLabel(info_line, x, label_y);
|
||||
}
|
||||
|
||||
// Move focus back to password input
|
||||
active_input = .password;
|
||||
if (!config.ly.hide_key_hints) {
|
||||
var length: u64 = 0;
|
||||
|
||||
if (c.dgn_output_code() != c.DGN_PAM) {
|
||||
buffer.info_line = c.dgn_output_log();
|
||||
}
|
||||
buffer.drawLabel(config.ly.shutdown_key, length, 0);
|
||||
length += config.ly.shutdown_key.len + 1;
|
||||
|
||||
if (config.ly_config.ly.blank_password) {
|
||||
c.input_text_clear(password);
|
||||
buffer.drawLabel(lang.ly.shutdown, length, 0);
|
||||
length += lang.ly.shutdown.len + 1;
|
||||
|
||||
buffer.drawLabel(config.ly.restart_key, length, 0);
|
||||
length += config.ly.restart_key.len + 1;
|
||||
|
||||
buffer.drawLabel(lang.ly.restart, length, 0);
|
||||
length += lang.ly.restart.len + 1;
|
||||
}
|
||||
|
||||
draw_lock_state: {
|
||||
const lock_state = interop.getLockState(allocator, config.ly.console_dev) catch |err| {
|
||||
if (err == error.CannotOpenConsoleDev) {
|
||||
info_line = lang.ly.err_console_dev;
|
||||
} else {
|
||||
info_line = lang.ly.err_alloc;
|
||||
}
|
||||
break :draw_lock_state;
|
||||
};
|
||||
|
||||
c.dgn_reset();
|
||||
} else {
|
||||
buffer.info_line = c_lang.logout;
|
||||
}
|
||||
var lock_state_x = buffer.width - lang.ly.numlock.len;
|
||||
const lock_state_y: u64 = if (has_clock) 1 else 0;
|
||||
|
||||
try utils.load(desktop, username);
|
||||
if (lock_state.numlock) buffer.drawLabel(lang.ly.numlock, lock_state_x, lock_state_y);
|
||||
lock_state_x -= lang.ly.capslock.len + 1;
|
||||
if (lock_state.capslock) buffer.drawLabel(lang.ly.capslock, lock_state_x, lock_state_y);
|
||||
}
|
||||
|
||||
// Reset cursor to its normal state
|
||||
_ = std.ChildProcess.exec(.{ .argv = &[_][]const u8{ "/usr/bin/tput", "cnorm" }, .allocator = allocator }) catch return;
|
||||
if (resolution_changed) {
|
||||
const coordinates = buffer.calculateComponentCoordinates();
|
||||
desktop.position(coordinates.x, coordinates.y + 2, coordinates.visible_length);
|
||||
login.position(coordinates.x, coordinates.y + 4, coordinates.visible_length);
|
||||
password.position(coordinates.x, coordinates.y + 6, coordinates.visible_length);
|
||||
|
||||
update = true;
|
||||
},
|
||||
else => {
|
||||
update = true;
|
||||
},
|
||||
resolution_changed = false;
|
||||
}
|
||||
|
||||
desktop.draw();
|
||||
login.draw();
|
||||
password.drawMasked(config.ly.asterisk);
|
||||
|
||||
update = animate;
|
||||
} else {
|
||||
std.time.sleep(10_000_000);
|
||||
update = buffer.cascade();
|
||||
|
||||
if (!update) {
|
||||
std.time.sleep(7_000_000_000);
|
||||
auth_fails = 0;
|
||||
}
|
||||
}
|
||||
|
||||
termbox.tb_present();
|
||||
}
|
||||
}
|
||||
|
||||
// Stop termbox
|
||||
c.tb_shutdown();
|
||||
var timeout: i32 = -1;
|
||||
|
||||
// Free inputs
|
||||
c.input_desktop_free(desktop);
|
||||
c.input_text_free(username);
|
||||
c.input_text_free(password);
|
||||
c.free_hostname();
|
||||
// Calculate the maximum timeout based on current animations, or the (big) clock. If there's none, we wait for the event indefinitely instead
|
||||
if (animate) {
|
||||
timeout = config.ly.min_refresh_delta;
|
||||
} else if (config.ly.bigclock and config.ly.clock.len == 0) {
|
||||
var tv = std.mem.zeroes(std.c.timeval);
|
||||
_ = std.c.gettimeofday(&tv, null);
|
||||
|
||||
// Unload configuration
|
||||
c.draw_free(buffer);
|
||||
config.lang_free();
|
||||
timeout = @intCast((60 - @rem(tv.tv_sec, 60)) * 1000 - @divTrunc(tv.tv_usec, 1000) + 1);
|
||||
} else if (config.ly.clock.len > 0 or auth_fails >= 10) {
|
||||
var tv = std.mem.zeroes(std.c.timeval);
|
||||
_ = std.c.gettimeofday(&tv, null);
|
||||
|
||||
if (shutdown) {
|
||||
var shutdown_cmd = try std.fmt.allocPrint(allocator, "{s}", .{config.ly_config.ly.shutdown_cmd});
|
||||
// This will never be freed! But it's fine, we're shutting down the system anyway
|
||||
defer allocator.free(shutdown_cmd);
|
||||
timeout = @intCast(1000 - @divTrunc(tv.tv_usec, 1000) + 1);
|
||||
}
|
||||
|
||||
config.config_free();
|
||||
const event_error = if (timeout == -1) termbox.tb_poll_event(&event) else termbox.tb_peek_event(&event, timeout);
|
||||
|
||||
std.process.execv(allocator, &[_][]const u8{ "/bin/sh", "-c", shutdown_cmd }) catch return;
|
||||
} else if (reboot) {
|
||||
var restart_cmd = try std.fmt.allocPrint(allocator, "{s}", .{config.ly_config.ly.restart_cmd});
|
||||
// This will never be freed! But it's fine, we're rebooting the system anyway
|
||||
defer allocator.free(restart_cmd);
|
||||
if (event_error < 0 or event.type != termbox.TB_EVENT_KEY) continue;
|
||||
|
||||
config.config_free();
|
||||
switch (event.key) {
|
||||
termbox.TB_KEY_F1, termbox.TB_KEY_F2, termbox.TB_KEY_F3, termbox.TB_KEY_F4, termbox.TB_KEY_F5, termbox.TB_KEY_F6, termbox.TB_KEY_F7, termbox.TB_KEY_F8, termbox.TB_KEY_F9, termbox.TB_KEY_F10, termbox.TB_KEY_F11, termbox.TB_KEY_F12 => {
|
||||
if (0xFFFF - event.key + 1 == shutdown_key) {
|
||||
shutdown = true;
|
||||
run = false;
|
||||
} else if (0xFFFF - event.key + 1 == restart_key) {
|
||||
restart = true;
|
||||
run = false;
|
||||
}
|
||||
},
|
||||
termbox.TB_KEY_CTRL_C => run = false,
|
||||
termbox.TB_KEY_CTRL_U => {
|
||||
if (active_input == .login) {
|
||||
login.clear();
|
||||
update = true;
|
||||
} else if (active_input == .password) {
|
||||
password.clear();
|
||||
update = true;
|
||||
}
|
||||
},
|
||||
termbox.TB_KEY_CTRL_K, termbox.TB_KEY_ARROW_UP => {
|
||||
active_input = switch (active_input) {
|
||||
.session, .login => .session,
|
||||
.password => .login,
|
||||
};
|
||||
update = true;
|
||||
},
|
||||
termbox.TB_KEY_CTRL_J, termbox.TB_KEY_ARROW_DOWN => {
|
||||
active_input = switch (active_input) {
|
||||
.session => .login,
|
||||
.login, .password => .password,
|
||||
};
|
||||
update = true;
|
||||
},
|
||||
termbox.TB_KEY_TAB => {
|
||||
active_input = switch (active_input) {
|
||||
.session => .login,
|
||||
.login => .password,
|
||||
.password => .session,
|
||||
};
|
||||
update = true;
|
||||
},
|
||||
termbox.TB_KEY_ENTER => authenticate: {
|
||||
if (config.ly.save) save_last_settings: {
|
||||
var file = std.fs.createFileAbsolute(config.ly.save_file, .{}) catch break :save_last_settings;
|
||||
defer file.close();
|
||||
|
||||
const writer = file.writer();
|
||||
try writer.writeIntLittle(u64, login.end);
|
||||
_ = try writer.write(login.text.items);
|
||||
try writer.writeIntLittle(u64, desktop.current);
|
||||
}
|
||||
|
||||
std.process.execv(allocator, &[_][]const u8{ "/bin/sh", "-c", restart_cmd }) catch return;
|
||||
} else {
|
||||
config.config_free();
|
||||
var has_error = false;
|
||||
|
||||
auth.authenticate(allocator, config.ly.tty, buffer, desktop, login, password) catch {
|
||||
has_error = true;
|
||||
auth_fails += 1;
|
||||
active_input = .password;
|
||||
|
||||
// TODO: Errors in info_line
|
||||
|
||||
if (config.ly.blank_password) password.clear();
|
||||
};
|
||||
update = true;
|
||||
|
||||
if (!has_error) info_line = lang.ly.logout;
|
||||
std.process.execv(allocator, &[_][]const u8{ "/bin/sh", "-c", config.ly.term_restore_cursor_cmd }) catch break :authenticate;
|
||||
},
|
||||
else => {
|
||||
switch (active_input) {
|
||||
.session => desktop.handle(&event),
|
||||
.login => login.handle(&event) catch {
|
||||
info_line = lang.ly.err_alloc;
|
||||
},
|
||||
.password => password.handle(&event) catch {
|
||||
info_line = lang.ly.err_alloc;
|
||||
},
|
||||
}
|
||||
update = true;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Low-level error messages
|
||||
fn log_init(log: [*c][*c]u8) void {
|
||||
log[c.DGN_OK] = c_lang.err_dgn_oob;
|
||||
log[c.DGN_NULL] = c_lang.err_null;
|
||||
log[c.DGN_ALLOC] = c_lang.err_alloc;
|
||||
log[c.DGN_BOUNDS] = c_lang.err_bounds;
|
||||
log[c.DGN_DOMAIN] = c_lang.err_domain;
|
||||
log[c.DGN_MLOCK] = c_lang.err_mlock;
|
||||
log[c.DGN_XSESSIONS_DIR] = c_lang.err_xsessions_dir;
|
||||
log[c.DGN_XSESSIONS_OPEN] = c_lang.err_xsessions_open;
|
||||
log[c.DGN_PATH] = c_lang.err_path;
|
||||
log[c.DGN_CHDIR] = c_lang.err_chdir;
|
||||
log[c.DGN_PWNAM] = c_lang.err_pwnam;
|
||||
log[c.DGN_USER_INIT] = c_lang.err_user_init;
|
||||
log[c.DGN_USER_GID] = c_lang.err_user_gid;
|
||||
log[c.DGN_USER_UID] = c_lang.err_user_uid;
|
||||
log[c.DGN_PAM] = c_lang.err_pam;
|
||||
log[c.DGN_HOSTNAME] = c_lang.err_hostname;
|
||||
if (shutdown) {
|
||||
return std.process.execv(allocator, &[_][]const u8{ "/bin/sh", "-c", config.ly.shutdown_cmd });
|
||||
} else if (restart) {
|
||||
return std.process.execv(allocator, &[_][]const u8{ "/bin/sh", "-c", config.ly.restart_cmd });
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,169 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const interop = @import("../interop.zig");
|
||||
const utils = @import("utils.zig");
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
const Random = std.rand.Random;
|
||||
|
||||
const termbox = interop.termbox;
|
||||
|
||||
const TerminalBuffer = @This();
|
||||
|
||||
random: Random,
|
||||
width: u64,
|
||||
height: u64,
|
||||
buffer: [*]termbox.tb_cell,
|
||||
fg: u8,
|
||||
bg: u8,
|
||||
box_chars: struct {
|
||||
left_up: u32,
|
||||
left_down: u32,
|
||||
right_up: u32,
|
||||
right_down: u32,
|
||||
top: u32,
|
||||
bottom: u32,
|
||||
left: u32,
|
||||
right: u32,
|
||||
},
|
||||
labels_max_length: u64,
|
||||
box_x: u64,
|
||||
box_y: u64,
|
||||
box_width: u64,
|
||||
box_height: u64,
|
||||
margin_box_v: u8,
|
||||
margin_box_h: u8,
|
||||
|
||||
pub fn init(margin_box_v: u8, margin_box_h: u8, input_length: u8, labels_max_length: u64, fg: u8, bg: u8) TerminalBuffer {
|
||||
var prng = std.rand.Isaac64.init(@intCast(std.time.timestamp()));
|
||||
|
||||
return .{
|
||||
.random = prng.random(),
|
||||
.width = @intCast(termbox.tb_width()),
|
||||
.height = @intCast(termbox.tb_height()),
|
||||
.buffer = termbox.tb_cell_buffer(),
|
||||
.fg = fg,
|
||||
.bg = bg,
|
||||
.box_chars = if (builtin.os.tag == .linux or builtin.os.tag.isBSD()) .{
|
||||
.left_up = 0x250C,
|
||||
.left_down = 0x2514,
|
||||
.right_up = 0x2510,
|
||||
.right_down = 0x2518,
|
||||
.top = 0x2500,
|
||||
.bottom = 0x2500,
|
||||
.left = 0x2502,
|
||||
.right = 0x2502,
|
||||
} else .{
|
||||
.left_up = '+',
|
||||
.left_down = '+',
|
||||
.right_up = '+',
|
||||
.right_down = '+',
|
||||
.top = '-',
|
||||
.bottom = '-',
|
||||
.left = '|',
|
||||
.right = '|',
|
||||
},
|
||||
.labels_max_length = labels_max_length,
|
||||
.box_x = 0,
|
||||
.box_y = 0,
|
||||
.box_width = (2 * margin_box_h) + input_length + 1 + labels_max_length,
|
||||
.box_height = 7 + (2 * margin_box_v),
|
||||
.margin_box_v = margin_box_v,
|
||||
.margin_box_h = margin_box_h,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn cascade(self: TerminalBuffer) bool {
|
||||
var changes = false;
|
||||
|
||||
var y = self.height - 2;
|
||||
while (y > 0) : (y -= 1) {
|
||||
for (0..self.width) |x| {
|
||||
const c: u8 = @truncate(self.buffer[(y - 1) * self.width + x].ch);
|
||||
if (std.ascii.isWhitespace(c)) continue;
|
||||
|
||||
const c_under: u8 = @truncate(self.buffer[y * self.width + x].ch);
|
||||
if (!std.ascii.isWhitespace(c_under)) continue;
|
||||
|
||||
changes = true;
|
||||
|
||||
if ((self.random.int(u16) % 10) > 7) continue;
|
||||
|
||||
self.buffer[y * self.width + x] = self.buffer[(y - 1) * self.width + x];
|
||||
self.buffer[(y - 1) * self.width + x].ch = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
pub fn drawBoxCenter(self: *TerminalBuffer, show_borders: bool, blank_box: bool) void {
|
||||
const x1 = (self.width - self.box_width) / 2;
|
||||
const y1 = (self.height - self.box_height) / 2;
|
||||
const x2 = (self.width + self.box_width) / 2;
|
||||
const y2 = (self.height + self.box_height) / 2;
|
||||
|
||||
self.box_x = x1;
|
||||
self.box_y = y1;
|
||||
|
||||
if (show_borders) {
|
||||
termbox.tb_change_cell(@intCast(x1 - 1), @intCast(y1 - 1), self.box_chars.left_up, self.fg, self.bg);
|
||||
termbox.tb_change_cell(@intCast(x2), @intCast(y1 - 1), self.box_chars.right_up, self.fg, self.bg);
|
||||
termbox.tb_change_cell(@intCast(x1 - 1), @intCast(y2), self.box_chars.left_down, self.fg, self.bg);
|
||||
termbox.tb_change_cell(@intCast(x2), @intCast(y2), self.box_chars.right_down, self.fg, self.bg);
|
||||
|
||||
var c1 = utils.initCell(self.box_chars.top, self.fg, self.bg);
|
||||
var c2 = utils.initCell(self.box_chars.bottom, self.fg, self.bg);
|
||||
|
||||
for (0..self.box_width) |i| {
|
||||
termbox.tb_put_cell(@intCast(x1 + i), @intCast(y1 - 1), &c1);
|
||||
termbox.tb_put_cell(@intCast(x1 + i), @intCast(y2), &c2);
|
||||
}
|
||||
|
||||
c1.ch = self.box_chars.left;
|
||||
c2.ch = self.box_chars.right;
|
||||
|
||||
for (0..self.box_height) |i| {
|
||||
termbox.tb_put_cell(@intCast(x1 - 1), @intCast(y1 + i), &c1);
|
||||
termbox.tb_put_cell(@intCast(x2), @intCast(y1 + i), &c2);
|
||||
}
|
||||
}
|
||||
|
||||
if (blank_box) {
|
||||
const blank = utils.initCell(' ', self.fg, self.bg);
|
||||
|
||||
for (0..self.box_height) |y| {
|
||||
for (0..self.box_width) |x| {
|
||||
termbox.tb_put_cell(@intCast(x1 + x), @intCast(y1 + y), &blank);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn calculateComponentCoordinates(self: TerminalBuffer) struct {
|
||||
x: u64,
|
||||
y: u64,
|
||||
visible_length: u64,
|
||||
} {
|
||||
const x = self.box_x + self.margin_box_h + self.labels_max_length + 1;
|
||||
const y = self.box_y + self.margin_box_v;
|
||||
const visible_length = self.box_x + self.box_width - self.margin_box_h - x;
|
||||
|
||||
return .{
|
||||
.x = x,
|
||||
.y = y,
|
||||
.visible_length = visible_length,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn drawLabel(self: TerminalBuffer, text: []const u8, x: u64, y: u64) void {
|
||||
const yc: c_int = @intCast(y);
|
||||
for (0..text.len) |xx| termbox.tb_change_cell(@intCast(x + xx), yc, text[xx], self.fg, self.bg);
|
||||
}
|
||||
|
||||
pub fn drawCharMultiple(self: TerminalBuffer, char: u8, x: u64, y: u64, length: u64) void {
|
||||
const yc: c_int = @intCast(y);
|
||||
const cell = utils.initCell(char, self.fg, self.bg);
|
||||
|
||||
for (0..length) |xx| termbox.tb_put_cell(@intCast(x + xx), yc, &cell);
|
||||
}
|
@ -0,0 +1,170 @@
|
||||
const std = @import("std");
|
||||
const ini = @import("ini");
|
||||
const enums = @import("../../enums.zig");
|
||||
const interop = @import("../../interop.zig");
|
||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
const EnvironmentList = std.ArrayList(Environment);
|
||||
|
||||
const DisplayServer = enums.DisplayServer;
|
||||
|
||||
const termbox = interop.termbox;
|
||||
|
||||
pub const DESKTOP_ENTRY_MAX_SIZE = 8 * 1024;
|
||||
|
||||
const Desktop = @This();
|
||||
|
||||
pub const Environment = struct {
|
||||
has_entry_buffer: bool,
|
||||
entry_buffer: []u8,
|
||||
name: []const u8,
|
||||
xdg_name: []const u8,
|
||||
cmd: []const u8,
|
||||
specifier: []const u8,
|
||||
display_server: DisplayServer,
|
||||
};
|
||||
|
||||
pub const Entry = struct {
|
||||
Desktop_Entry: struct {
|
||||
Exec: []const u8,
|
||||
Name: []const u8,
|
||||
},
|
||||
};
|
||||
|
||||
allocator: Allocator,
|
||||
buffer: *TerminalBuffer,
|
||||
environments: EnvironmentList,
|
||||
current: u64,
|
||||
visible_length: u64,
|
||||
x: u64,
|
||||
y: u64,
|
||||
|
||||
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, max_length: u64) !Desktop {
|
||||
return .{
|
||||
.allocator = allocator,
|
||||
.buffer = buffer,
|
||||
.environments = try EnvironmentList.initCapacity(allocator, max_length),
|
||||
.current = 0,
|
||||
.visible_length = 0,
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: Desktop) void {
|
||||
for (self.environments.items) |environment| {
|
||||
if (environment.has_entry_buffer) self.allocator.free(environment.entry_buffer);
|
||||
}
|
||||
|
||||
self.environments.deinit();
|
||||
}
|
||||
|
||||
pub fn position(self: *Desktop, x: u64, y: u64, visible_length: u64) void {
|
||||
self.x = x;
|
||||
self.y = y;
|
||||
self.visible_length = visible_length;
|
||||
}
|
||||
|
||||
pub fn addEnvironment(self: *Desktop, name: []const u8, cmd: []const u8, display_server: DisplayServer) !void {
|
||||
try self.environments.append(.{
|
||||
.has_entry_buffer = false,
|
||||
.entry_buffer = undefined,
|
||||
.name = name,
|
||||
.xdg_name = name, // TODO
|
||||
.cmd = cmd,
|
||||
.specifier = switch (display_server) {
|
||||
.wayland => "wayland",
|
||||
.x11 => "x11",
|
||||
else => "other",
|
||||
},
|
||||
.display_server = display_server,
|
||||
});
|
||||
|
||||
self.current = self.environments.items.len - 1;
|
||||
}
|
||||
|
||||
pub fn addEnvironmentWithBuffer(self: *Desktop, entry_buffer: []u8, name: []const u8, cmd: []const u8, display_server: DisplayServer) !void {
|
||||
try self.environments.append(.{
|
||||
.has_entry_buffer = true,
|
||||
.entry_buffer = entry_buffer,
|
||||
.name = name,
|
||||
.xdg_name = name, // TODO
|
||||
.cmd = cmd,
|
||||
.specifier = switch (display_server) {
|
||||
.wayland => "wayland",
|
||||
.x11 => "x11",
|
||||
else => "other",
|
||||
},
|
||||
.display_server = display_server,
|
||||
});
|
||||
|
||||
self.current = self.environments.items.len - 1;
|
||||
}
|
||||
|
||||
pub fn crawl(self: *Desktop, path: []const u8, display_server: DisplayServer) !void {
|
||||
var directory = try std.fs.openDirAbsolute(path, .{});
|
||||
defer directory.close();
|
||||
|
||||
var iterable_directory = try std.fs.openIterableDirAbsolute(path, .{});
|
||||
defer iterable_directory.close();
|
||||
|
||||
var iterator = iterable_directory.iterate();
|
||||
while (try iterator.next()) |item| {
|
||||
var file = try directory.openFile(item.name, .{});
|
||||
defer file.close();
|
||||
|
||||
const buffer = try file.readToEndAlloc(self.allocator, DESKTOP_ENTRY_MAX_SIZE);
|
||||
const entry = try ini.readToStruct(Entry, buffer);
|
||||
|
||||
try self.addEnvironmentWithBuffer(buffer, entry.Desktop_Entry.Name, entry.Desktop_Entry.Exec, display_server);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle(self: *Desktop, maybe_event: ?*termbox.tb_event) void {
|
||||
if (maybe_event) |event| blk: {
|
||||
if (event.type != termbox.TB_EVENT_KEY) break :blk;
|
||||
|
||||
switch (event.key) {
|
||||
termbox.TB_KEY_ARROW_LEFT, termbox.TB_KEY_CTRL_H => self.goLeft(),
|
||||
termbox.TB_KEY_ARROW_RIGHT, termbox.TB_KEY_CTRL_L => self.goRight(),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
termbox.tb_set_cursor(@intCast(self.x + 2), @intCast(self.y));
|
||||
}
|
||||
|
||||
pub fn draw(self: Desktop) void {
|
||||
const environment = self.environments.items[self.current];
|
||||
|
||||
const length = @min(environment.name.len, self.visible_length - 3);
|
||||
if (length == 0) return;
|
||||
|
||||
const x = self.buffer.box_x + self.buffer.margin_box_h;
|
||||
const y = self.buffer.box_y + self.buffer.margin_box_v + 2;
|
||||
self.buffer.drawLabel(environment.specifier, x, y);
|
||||
|
||||
termbox.tb_change_cell(@intCast(self.x), @intCast(self.y), '<', self.buffer.fg, self.buffer.bg);
|
||||
termbox.tb_change_cell(@intCast(self.x + self.visible_length - 1), @intCast(self.y), '>', self.buffer.fg, self.buffer.bg);
|
||||
|
||||
self.buffer.drawLabel(environment.name, self.x + 2, self.y);
|
||||
}
|
||||
|
||||
fn goLeft(self: *Desktop) void {
|
||||
if (self.current == 0) {
|
||||
self.current = self.environments.items.len - 1;
|
||||
return;
|
||||
}
|
||||
|
||||
self.current -= 1;
|
||||
}
|
||||
|
||||
fn goRight(self: *Desktop) void {
|
||||
if (self.current == self.environments.items.len - 1) {
|
||||
self.current = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
self.current += 1;
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
const std = @import("std");
|
||||
const interop = @import("../../interop.zig");
|
||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||
const utils = @import("../utils.zig");
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
const DynamicString = std.ArrayList(u8);
|
||||
|
||||
const termbox = interop.termbox;
|
||||
|
||||
const Text = @This();
|
||||
|
||||
allocator: Allocator,
|
||||
buffer: *TerminalBuffer,
|
||||
text: DynamicString,
|
||||
end: u64,
|
||||
cursor: u64,
|
||||
visible_start: u64,
|
||||
visible_length: u64,
|
||||
x: u64,
|
||||
y: u64,
|
||||
|
||||
pub fn init(allocator: Allocator, buffer: *TerminalBuffer, max_length: u64) !Text {
|
||||
const text = try DynamicString.initCapacity(allocator, max_length);
|
||||
|
||||
return .{
|
||||
.allocator = allocator,
|
||||
.buffer = buffer,
|
||||
.text = text,
|
||||
.end = 0,
|
||||
.cursor = 0,
|
||||
.visible_start = 0,
|
||||
.visible_length = 0,
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: Text) void {
|
||||
self.text.deinit();
|
||||
}
|
||||
|
||||
pub fn position(self: *Text, x: u64, y: u64, visible_length: u64) void {
|
||||
self.x = x;
|
||||
self.y = y;
|
||||
self.visible_length = visible_length;
|
||||
}
|
||||
|
||||
pub fn handle(self: *Text, maybe_event: ?*termbox.tb_event) !void {
|
||||
if (maybe_event) |event| blk: {
|
||||
if (event.type != termbox.TB_EVENT_KEY) break :blk;
|
||||
|
||||
switch (event.key) {
|
||||
termbox.TB_KEY_ARROW_LEFT => self.goLeft(),
|
||||
termbox.TB_KEY_ARROW_RIGHT => self.goRight(),
|
||||
termbox.TB_KEY_DELETE => self.delete(),
|
||||
termbox.TB_KEY_BACKSPACE, termbox.TB_KEY_BACKSPACE2 => self.backspace(),
|
||||
termbox.TB_KEY_SPACE => try self.write(' '),
|
||||
else => if (event.ch > 31 and event.ch < 127) try self.write(@intCast(event.ch)),
|
||||
}
|
||||
}
|
||||
|
||||
termbox.tb_set_cursor(@intCast(self.x + (self.cursor - self.visible_start)), @intCast(self.y));
|
||||
}
|
||||
|
||||
pub fn draw(self: Text) void {
|
||||
const length = @min(self.text.items.len, self.visible_length);
|
||||
if (length == 0) return;
|
||||
|
||||
const visible_slice = if (self.text.items.len > self.visible_length and self.cursor < self.text.items.len) self.text.items[self.visible_start..(self.visible_length + self.visible_start)] else self.text.items[self.visible_start..];
|
||||
self.buffer.drawLabel(visible_slice, self.x, self.y);
|
||||
}
|
||||
|
||||
pub fn drawMasked(self: Text, mask: u8) void {
|
||||
const length = @min(self.text.items.len, self.visible_length - 1);
|
||||
if (length == 0) return;
|
||||
|
||||
self.buffer.drawCharMultiple(mask, self.x, self.y, length);
|
||||
}
|
||||
|
||||
pub fn clear(self: *Text) void {
|
||||
self.text.clearRetainingCapacity();
|
||||
self.end = 0;
|
||||
self.cursor = 0;
|
||||
self.visible_start = 0;
|
||||
}
|
||||
|
||||
fn goLeft(self: *Text) void {
|
||||
if (self.cursor == 0) return;
|
||||
if (self.visible_start > 0) self.visible_start -= 1;
|
||||
|
||||
self.cursor -= 1;
|
||||
}
|
||||
|
||||
fn goRight(self: *Text) void {
|
||||
if (self.cursor >= self.end) return;
|
||||
if (self.cursor - self.visible_start == self.visible_length - 1) self.visible_start += 1;
|
||||
|
||||
self.cursor += 1;
|
||||
}
|
||||
|
||||
fn delete(self: *Text) void {
|
||||
_ = self.text.orderedRemove(self.cursor);
|
||||
|
||||
self.end -= 1;
|
||||
}
|
||||
|
||||
fn backspace(self: *Text) void {
|
||||
if (self.cursor == 0) return;
|
||||
|
||||
self.goLeft();
|
||||
self.delete();
|
||||
}
|
||||
|
||||
fn write(self: *Text, char: u8) !void {
|
||||
if (char == 0) return;
|
||||
|
||||
try self.text.insert(self.cursor, char);
|
||||
|
||||
self.end += 1;
|
||||
self.goRight();
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
const std = @import("std");
|
||||
const interop = @import("../interop.zig");
|
||||
|
||||
const termbox = interop.termbox;
|
||||
|
||||
pub fn initCell(ch: u32, fg: u32, bg: u32) termbox.tb_cell {
|
||||
var cell = std.mem.zeroes(termbox.tb_cell);
|
||||
cell.ch = ch;
|
||||
cell.fg = fg;
|
||||
cell.bg = bg;
|
||||
return cell;
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
#include "configator.h"
|
||||
#include "dragonfail.h"
|
||||
#include "inputs.h"
|
||||
#include "config.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
#include <sys/consio.h>
|
||||
#else // linux
|
||||
#include <linux/vt.h>
|
||||
#endif
|
||||
|
||||
static char* hostname_backup = NULL;
|
||||
|
||||
void hostname(char** out)
|
||||
{
|
||||
if (hostname_backup != NULL)
|
||||
{
|
||||
*out = hostname_backup;
|
||||
return;
|
||||
}
|
||||
|
||||
int maxlen = sysconf(_SC_HOST_NAME_MAX);
|
||||
|
||||
if (maxlen < 0)
|
||||
{
|
||||
maxlen = _POSIX_HOST_NAME_MAX;
|
||||
}
|
||||
|
||||
hostname_backup = malloc(maxlen + 1);
|
||||
|
||||
if (hostname_backup == NULL)
|
||||
{
|
||||
dgn_throw(DGN_ALLOC);
|
||||
return;
|
||||
}
|
||||
|
||||
if (gethostname(hostname_backup, maxlen) < 0)
|
||||
{
|
||||
dgn_throw(DGN_HOSTNAME);
|
||||
return;
|
||||
}
|
||||
|
||||
hostname_backup[maxlen] = '\0';
|
||||
*out = hostname_backup;
|
||||
}
|
||||
|
||||
void free_hostname()
|
||||
{
|
||||
free(hostname_backup);
|
||||
}
|
||||
|
||||
void switch_tty(struct term_buf* buf)
|
||||
{
|
||||
FILE* console = fopen(config.console_dev, "w");
|
||||
|
||||
if (console == NULL)
|
||||
{
|
||||
buf->info_line = lang.err_console_dev;
|
||||
return;
|
||||
}
|
||||
|
||||
int fd = fileno(console);
|
||||
|
||||
ioctl(fd, VT_ACTIVATE, config.tty);
|
||||
ioctl(fd, VT_WAITACTIVE, config.tty);
|
||||
|
||||
fclose(console);
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
#ifndef H_LY_UTILS
|
||||
#define H_LY_UTILS
|
||||
|
||||
#include "draw.h"
|
||||
#include "inputs.h"
|
||||
#include "config.h"
|
||||
|
||||
void desktop_load(struct desktop* target);
|
||||
void hostname(char** out);
|
||||
void free_hostname();
|
||||
void switch_tty(struct term_buf* buf);
|
||||
|
||||
#endif
|
@ -1,114 +0,0 @@
|
||||
const std = @import("std");
|
||||
const ini = @import("ini");
|
||||
const main = @import("main.zig");
|
||||
const config = @import("config.zig");
|
||||
const interop = @import("interop.zig");
|
||||
|
||||
const DESKTOP_ENTRY_MAX_SIZE: usize = 8 * 1024;
|
||||
|
||||
const Entry = struct {
|
||||
Desktop_Entry: struct {
|
||||
Name: []const u8,
|
||||
Comment: []const u8,
|
||||
Exec: []const u8,
|
||||
Type: []const u8,
|
||||
DesktopNames: []const u8,
|
||||
},
|
||||
};
|
||||
|
||||
pub export fn desktop_load(target: *main.c.struct_desktop) void {
|
||||
// We don't care about desktop environments presence
|
||||
// because the fallback shell is always available
|
||||
// so we just dismiss any "throw" for now
|
||||
var err: c_int = 0;
|
||||
|
||||
desktop_crawl(target, config.ly_config.ly.waylandsessions, main.c.DS_WAYLAND) catch {};
|
||||
|
||||
if (main.c.dgn_catch() != 0) {
|
||||
err += 1;
|
||||
main.c.dgn_reset();
|
||||
}
|
||||
|
||||
desktop_crawl(target, config.ly_config.ly.xsessions, main.c.DS_XORG) catch {};
|
||||
|
||||
if (main.c.dgn_catch() != 0) {
|
||||
err += 1;
|
||||
main.c.dgn_reset();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save(desktop: *main.c.struct_desktop, login: *main.c.struct_text) !void {
|
||||
if (!config.ly_config.ly.save) {
|
||||
return;
|
||||
}
|
||||
|
||||
var file = std.fs.openFileAbsolute(config.ly_config.ly.save_file, .{ .mode = .write_only }) catch {
|
||||
return;
|
||||
};
|
||||
defer file.close();
|
||||
|
||||
var buffer = try std.fmt.allocPrint(main.allocator, "{s}\n{d}", .{ login.*.text, desktop.*.cur });
|
||||
defer main.allocator.free(buffer);
|
||||
|
||||
try file.writeAll(buffer);
|
||||
}
|
||||
|
||||
pub fn load(desktop: *main.c.struct_desktop, login: *main.c.struct_text) !void {
|
||||
if (!config.ly_config.ly.load) {
|
||||
return;
|
||||
}
|
||||
|
||||
var file = std.fs.openFileAbsolute(config.ly_config.ly.save_file, .{}) catch {
|
||||
return;
|
||||
};
|
||||
defer file.close();
|
||||
|
||||
var buffer = try main.allocator.alloc(u8, config.ly_config.ly.max_login_len * 2 + 1);
|
||||
defer main.allocator.free(buffer);
|
||||
|
||||
_ = try file.readAll(buffer);
|
||||
|
||||
var array = std.mem.splitSequence(u8, buffer, "\n");
|
||||
|
||||
login.*.text = try interop.c_str(array.first()); // TODO: Free?
|
||||
desktop.*.cur = try std.fmt.parseUnsigned(u16, array.next().?, 0);
|
||||
}
|
||||
|
||||
fn desktop_crawl(target: *main.c.struct_desktop, sessions: []const u8, server: main.c.enum_display_server) !void {
|
||||
var iterable_dir = std.fs.openIterableDirAbsolute(sessions, .{}) catch {
|
||||
main.c.dgn_throw(main.c.DGN_XSESSIONS_OPEN);
|
||||
return;
|
||||
};
|
||||
defer iterable_dir.close();
|
||||
|
||||
var iterator = iterable_dir.iterate();
|
||||
|
||||
var dir = std.fs.openDirAbsolute(sessions, .{}) catch {
|
||||
main.c.dgn_throw(main.c.DGN_XSESSIONS_OPEN);
|
||||
return;
|
||||
};
|
||||
defer dir.close();
|
||||
|
||||
while (try iterator.next()) |item| {
|
||||
if (!std.mem.endsWith(u8, item.name, ".desktop")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var file = try dir.openFile(item.name, .{});
|
||||
defer file.close();
|
||||
|
||||
var buffer = try main.allocator.alloc(u8, DESKTOP_ENTRY_MAX_SIZE); // TODO: Free
|
||||
var length = try file.readAll(buffer);
|
||||
var entry = try ini.readToStruct(Entry, buffer[0..length]);
|
||||
|
||||
// TODO: If it's a wayland session, add " (Wayland)" to its name,
|
||||
// as long as it doesn't already contain that string
|
||||
|
||||
const name = entry.Desktop_Entry.Name;
|
||||
const exec = entry.Desktop_Entry.Exec;
|
||||
|
||||
if (name.len > 0 and exec.len > 0) {
|
||||
main.c.input_desktop_add(target, (try interop.c_str(name)).ptr, (try interop.c_str(exec)).ptr, server);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue