The Great Push (mfw)

This commit is contained in:
lda 2024-03-30 14:13:40 +01:00
parent 55395ec2cf
commit 04429888af
99 changed files with 12396 additions and 2111 deletions

4
.gitignore vendored
View File

@ -13,7 +13,3 @@ __pycache__/
*.sublime-workspace
.vscode
.ycm_extra_conf.py
# Screenshots
*.png
*.raw

View File

@ -12,6 +12,7 @@ find_package(Gint 2.9 REQUIRED)
find_package(JustUI 1.3.0 REQUIRED)
find_package(LibImg 2.1 REQUIRED)
find_package(cPNG 1.5.30 REQUIRED)
find_package(cZlib 1.2.5 REQUIRED)
AUX_SOURCE_DIRECTORY(src SOURCES)
# Shared assets, fx-9860G-only assets and fx-CG-50-only assets
@ -33,16 +34,19 @@ set(ASSETS_cg
assets-cg/tweak.png
assets-cg/tbold.png
assets-cg/bouncer.png
assets-cg/cirno.png
assets-cg/marie.png
assets-cg/mastrix-print.png
assets-cg/house.png
assets-cg/uf8x9
assets-cg/uf5x7
)
fxconv_declare_assets(${ASSETS} ${ASSETS_fx} ${ASSETS_cg} WITH_METADATA)
add_executable(mastrix ${SOURCES} ${ASSETS} ${ASSETS_${FXSDK_PLATFORM}})
include_directories(src/include)
target_compile_options(mastrix PRIVATE -Wall -Wextra -O0 -g -finstrument-functions)
target_link_libraries(mastrix Gint::Gint JustUI::JustUI LibImg::LibImg)
target_compile_options(mastrix PRIVATE -Wall -Wextra -Os -g -finstrument-functions)
target_link_libraries(mastrix Gint::Gint JustUI::JustUI LibImg::LibImg cPNG::cPNG cZlib::cZlib)
if("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G)
# honestly i dont have an fx-9860G to test with.

43
LICENSE Normal file
View File

@ -0,0 +1,43 @@
Ma's Trix:
Copyright (c) 2024 LDA
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
yxml:
Copyright (c) 2013-2014 Yoran Heling
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

4
TODO
View File

@ -1,4 +0,0 @@
- Commands (and a catalog system)
- Save user info to be loaded later
- Custom reactions
- ????

BIN
assets-cg/bouncer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
assets-cg/cirno.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
assets-cg/file.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 557 B

View File

@ -4,6 +4,9 @@ mastrix-print.png:
user.png:
type: bopti-image
name: user
marie.png:
type: libimg-image
name: default_pfp
hourglass.png:
type: bopti-image
name: hourglass
@ -38,3 +41,25 @@ goda.png:
goda_full.png:
type: libimg-image
name: goda_full
cirno.png:
type: bopti-image
name: cirno
house.png:
type: bopti-image
name: credits_image
uf8x9:
type: font
name: uf8x9
charset: unicode
grid.size: 8x11
grid.padding: 1
proportional: true
height: 9
uf5x7:
type: font
name: uf5x7
charset: unicode
grid.size: 5x7
grid.padding: 1

BIN
assets-cg/hourglass.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 744 B

BIN
assets-cg/house.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
assets-cg/marie.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
assets-cg/tbold.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 781 B

BIN
assets-cg/tweak.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 B

BIN
assets-cg/uf5x7/U+0020.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
assets-cg/uf5x7/U+00A0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
assets-cg/uf5x7/U+0100.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
assets-cg/uf5x7/U+0370.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
assets-cg/uf5x7/U+0400.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
assets-cg/uf5x7/U+16A0.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
assets-cg/uf5x7/U+2010.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
assets-cg/uf5x7/U+2070.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

BIN
assets-cg/uf5x7/U+20A0.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 672 B

BIN
assets-cg/uf5x7/U+2160.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

BIN
assets-cg/uf5x7/U+2190.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
assets-cg/uf5x7/U+2200.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
assets-cg/uf5x7/U+2440.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

BIN
assets-cg/uf5x7/U+25A0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
assets-cg/uf5x7/U+2800.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
assets-cg/uf5x7/U+3000.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 831 B

BIN
assets-cg/uf5x7/U+3040.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
assets-cg/uf5x7/U+30A0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
assets-cg/uf5x7/extra.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
assets-cg/uf8x9/.o Normal file

Binary file not shown.

BIN
assets-cg/uf8x9/U+0020.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
assets-cg/uf8x9/U+00A0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
assets-cg/uf8x9/U+0100.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
assets-cg/uf8x9/U+0370.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
assets-cg/uf8x9/U+0400.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
assets-cg/uf8x9/U+16A0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
assets-cg/uf8x9/U+2010.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

BIN
assets-cg/uf8x9/U+2070.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

BIN
assets-cg/uf8x9/U+20A0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

BIN
assets-cg/uf8x9/U+2160.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

BIN
assets-cg/uf8x9/U+2190.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
assets-cg/uf8x9/U+2200.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
assets-cg/user.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

554
src/DOM.c
View File

@ -1,554 +0,0 @@
/*
Header inclusions.
*/
#include <WebCalc.h>
/*
DomStyle manipulation.
*/
/**
* DomStyle_new
*
* Dynamically allocates, initializes, and returns a new DomStyle object.
* Hexadecimal notation in default value indicate properties that should
* be overwritten by the tag nature.
*
* @return Allocated and initialized object.
*/
struct DomStyle *DomStyle_new(void)
{
// Storing the size of a structure.
const int size = sizeof(struct DomStyle);
// Using a DomStyle structure pointer and a static default structure.
struct DomStyle *pointer = NULL;
struct DomStyle style = {
// White background and black font color.
0, 1,
// Automatic overflow on both axes and no resize.
3, 3, 0,
// No display, static positioning, no floating, and top
// vertical alignment.
0x0, 0, 0, 0,
// Position : top, right, bottom and left.
0x0, 0x0, 0x0, 0x0,
// Default width and width : value, minimum and maximum.
0x0, 0, 4095, 0x0, 0, 4095,
// Default padding, borders with border radius, and margins.
0, 0, 0, 0,
0, 0, 0, 0, 0,
0x0, 0, 0x0, 0,
// Border spacing on x and y.
0, 0,
// Font size, letter spacing, line height and text indent.
0x0, 1, 0x0, 0,
// Default cursor, visible and `o` list type.
1, 1, 7,
// Left text alignment, no decoration, default wrapping
// behavior, separate table borders and show empty cells.
0, 0, 0, 0, 0,
// Tab size and word spacing.
4, 0x0
};
// Allocating the needed size in the memory.
pointer = (struct DomStyle *)malloc(size);
// Returning a NULL pointer on alloc failure.
if(!pointer) return NULL;
// Copying the static data to the reserved location.
memcpy(pointer, &style, size);
// Returning the allocated pointer.
return pointer;
}
/*
DomAttribute manipulation.
*/
/**
* DomAttribute_new()
*
* Allocates a new attribute with the given parameters. If a null
* reference is given for at least name or value, nothing is allocated
* and an empty structure is returned.
*/
struct DomAttribute *DomAttribute_new(reference_t name, reference_t value)
{
// Using a DomAttribute structure pointer.
struct DomAttribute *at = NULL;
// Allocating the size of the structure in the memory.
at = (struct DomAttribute *)malloc(sizeof(struct DomAttribute));
// Returning a NULL pointer on alloc failure.
if(!at) return NULL;
// Returning an empty structure if one of the arguments is null.
if(refnull(name) || refnull(value))
{
// Setting NULL pointers.
at->name = NULL;
at->value = NULL;
// Returning the structure.
return at;
}
// If given arguments are valid, extracting the referenced data.
at->name = refxtrct(name);
at->value = refxtrct(value);
// Returning the obtained structure. No alloc failure handling is added
// because it could not do anything more that returning the pointer.
return at;
}
/**
* DomAttribute_free()
*
* Frees the memory allocated for a DomAttribute object. As a convention,
* the structure itself is freed.
*
* @param at Attribute to free.
*/
void DomAttribute_free(struct DomAttribute *at)
{
// Returning is the argument is not valid.
if(!at) return;
// Freeing the name and value pointers, if valid.
if(at->name) free(at->name);
if(at->value) free(at->value);
// Freeing the structure itself.
free(at);
}
/*
DomElement object
*/
/**
* DomElement_new()
*
* Allocates and constructs a new DomElement, using the given parameters.
*
* @param name Element tag name.
* @param args Argument string.
* @param parent Parent node in DOM tree.
* @param previous Previous sibling in DOM tree.
*
* @return Allocated object, or NULL on failure.
*/
struct DomElement *DomElement_new(reference_t name, reference_t args,
struct DomElement *parent, struct DomElement *previous)
{
// Using a DomElement pointer.
struct DomElement *el;
// Using a DomStyle pointer to test allocation validity.
struct DomStyle *style;
// Allocating the pointer.
el = (struct DomElement *)malloc(sizeof(struct DomElement));
// Handling alloc failure by returning NULL without doing anything.
if(!el) return NULL;
// Immediately getting a style structure (to return on failure).
style = DomStyle_new();
// Handling alloc failure by canceling the operation.
if(!style)
{
// Freeing the allocated element.
free(el);
// Returning from the function.
return NULL;
}
// Setting the style structure pointer.
el->style = style;
// Extracting the tag name.
el->tagName = refxtrct(name);
// Setting a default textNode (or tex, basically even with the
// uncertainty on union manipulation this should be the same).
el->textNode = NULL;
// Setting default values for children.
el->children = NULL;
el->childnum = 0;
// Setting default values for attributes.
el->attributes = NULL;
el->attrnum = 0;
// Setting the parent and previous sibling.
el->parent = parent;
el->previous = previous;
// Returning the allocated structure.
return el;
}
/**
* DomElement_free()
*
* Frees a DomElement structure and all the associated allocate data. All
* its children are freed. As a convention, the structure itself is also
* freed.
*
* @param el Element structure to free.
*/
void DomElement_free(struct DomElement *el)
{
// Using an iterator.
int i;
// Freeing the tex node data.
/*if(!strcmp(el->tagName, "math")) tex_free(el->tex);*/
// Freeing the node content.
/*else */if(el->textNode) free(el->textNode);
// Freeing the tag name, if not NULL.
if(el->tagName) free(el->tagName);
// Freeing the style structure.
free(el->style);
// Freeing the tag attributes and the array.
for(i=0; i < el->attrnum; i++) DomAttribute_free(el->attributes[i]);
if(el->attributes) free(el->attributes);
// Freeing the children and the array.
for(i=0; i < el->childnum; i++) DomElement_free(el->children[i]);
if(el->children) free(el->children);
// Finally freeing the structure itself.
free(el);
}
/**
* DomElement_addChild()
*
* Adds a child to an element. Needs to reallocate the children array.
*
* @param el Parent element.
* @param child Child to add.
*
* @return Argument `child`, or NULL on failure.
*/
struct DomElement *DomElement_addChild(struct DomElement *el,
struct DomElement *child)
{
// Using a pointer array pointer.
struct DomElement **array;
// Using a variable to store the number of children.
int n;
// Checking arguments validity. Returning NULL on error.
if(!el || !child) return NULL;
// Getting the number of children.
n = el->childnum;
// Allocating a new array, freeing the old one on success.
array = realloc(el->children, (n + 1) * sizeof(struct DomElement *));
// Canceling operation on failure.
if(!array) return NULL;
// Adding the new child.
array[n] = child;
// Updating parent information.
el->children = array;
el->childnum++;
// Returning the child element.
return child;
}
/**
* DomElement_getChild()
*
* Returns the pointer on an element's child, represented by its position
* in the parent child list.
*
* @param el Element whose child is wanted.
* @param n Number of wanted child.
*
* @return Pointer of child element if exists, NULL otherwise.
*/
struct DomElement *DomElement_getChild(struct DomElement *el, int n)
{
// Using an integer to store the number of children.
int children;
// Checking argument validity.
if(!el) return NULL;
// Getting the number of children.
children = el->childnum;
if(children == 0)
{
printf("Oh, so unfortunate %s, for you have no children\n", el->tagName);
return NULL;
}
// Adapting range of n.
if(n < 0) n += children;
n %= children;
// Returning the wanted child.
return el->children[n];
}
/**
* DOM_sybl()
*/
static int DOM_sybl(reference_t name)
{
const char *names[] = { "amp", "lt", "gt", NULL };
const char symbols[] = { '&', '<', '>' };
const char **ptr = names;
while(*ptr) if(!refscmp(name, *ptr++)) return symbols[ptr - names - 1];
return ' ';
}
/**
* DomElement_setTextNode()
*
* Sets the text node of an element. If a text node already exists, it is
* freed. To remove the text node of an element, just call setTextNode(
* NULL).
*
* @param el Element whose text node should be set.
* @param text Text content.
*/
void DomElement_setTextNode(struct DomElement *el, reference_t text)
{
char *txt;
const char *ptr;
int length = 0, i = 0;
ptr = text.begin;
while(ptr < text.end)
{
if(*ptr == '&')
{
while(ptr < text.end && *ptr != ';') ptr++;
if(ptr == text.end) break;
}
length++;
ptr++;
}
txt = malloc(length + 1);
ptr = text.begin;
while(ptr < text.end)
{
if(*ptr == '&')
{
const char *end = ptr;
reference_t sybl = { ptr + 1, ptr };
while(end < text.end && *end != ';') end++;
sybl.end = end;
*(txt + i++) = DOM_sybl(sybl);
if(end == text.end) break;
ptr = end + 1;
continue;
}
*(txt + i++) = *ptr++;
}
*(txt + i) = 0;
/*if(!strcmp(el->tagName, "math"))
{
el->tex = tex_load_pointer(txt, txt + i);
free(txt);
return;
}*/
el->textNode = txt;
}
/**
* DomElement_getAttribute()
*
* Returns the value associated to a given attribute name.
*
* @param el Concerned element.
* @param name Attribute name.
*
* @return Attribute value if exists, NULL otherwise.
*/
const char *DomElement_getAttribute(struct DomElement *el, const char *name)
{
// Using an attribute pointer.
struct DomAttribute *attribute;
// Using an iterator.
int i;
// Checking arguments validity.
if(!el || !name) return NULL;
// Seeking the wanted attribute.
for(i=0; i < el->attrnum; i++)
{
// Getting the current attribute.
attribute = el->attributes[i];
// Returning the value if the name corresponds.
if(!strcmp(attribute->name, name)) return attribute->value;
}
// If nothing is found, returning NULL;
return NULL;
}
/**
* DomElement_resolve()
*
* Recursively computes width and height of an element and all its
* children.
* Top and left coordinates indicate the position where the element is
* placed, including padding, borders and margins.
* Basic information (position, maximum width, etc.) of the current
* element should be set by the parent call of DomElement_resolve(). As
* a consequence, it is advised to use only this function on the html
* element, that is, by calling document_resolve().
*/
/* This is probably the kinds of affairs to be managed by JustUI tbh */
#if 0
void DomElement_resolve(struct DomElement *el)
{
// Using a DomElement structure pointer for iterating over children.
struct DomElement *child;
// Using two variables for coordinates handling and an iterator.
int x, y, i;
// Using an integer to store the maximum width of children.
int mw;
// Resolving math elements.
/*if(!strcmp(el->tagName,"math"))
{
// Setting the dimensions given by the natural display module.
el->style->width = el->tex->width;
el->style->height = el->tex->height;
}*/
// Resolving other text elements.
/*else if(el->textNode) Disp_text(el,1,0,0);*/
// Applying special behavior for each display style.
switch(el->style->display)
{
// Block elements.
case 0:
el->style->width = el->style->maxWidth;
}
// Returning immediately if the element hasn't any child.
if(!el->childnum) return;
// Resolving childrens.
for(i=0; i < el->childnum; i++)
{
// Getting the current child.
child = el->children[i];
// Calculating child's maximum width.
mw = el->style->maxWidth - DOMElement_tokenWidth(child);
// Setting its maximum width.
if(!child->style->maxWidth || child->style->maxWidth > mw)
child->style->maxWidth = mw;
// Resolving it. Then, width and height should exist.
DomElement_resolve(child);
}
// Initializing cursor position.
x = 0, y = 0;
// Positioning children.
for(i=0;i<el->childnum;i++)
{
// Getting the next child.
child = el->children[i];
// If next child is a block.
if(child->style->display == 0)
{
// Add child's total height.
child->style->left = x;
child->style->top = y;
y += DOMElement_totalHeight(child);
x = 0;
}
}
// Getting new information.
el->style->height = y;
}
#endif
//#############################################################################################################################
//
// D O M E l e m e n t O b j e c t
//
//#############################################################################################################################
int DomElement_get(struct DomElement *el, int n)
{
/*
** This function returns various informations about an element.
** n return value
** 1 total width
** 2 total height
** 3 border, padding, margin width
** 4 border, padding, margin height
*/
switch(n)
{
case 1:
return el->style->marginLeft + el->style->borderLeft + el->style->paddingLeft + el->style->width
+ el->style->paddingRight + el->style->borderRight + el->style->marginRight;
case 2:
return el->style->marginTop + el->style->borderTop + el->style->paddingTop + el->style->height
+ el->style->paddingBottom + el->style->borderBottom + el->style->marginBottom;
case 3:
return el->style->marginLeft + el->style->borderLeft + el->style->paddingLeft
+ el->style->paddingRight + el->style->borderRight + el->style->marginRight;
case 4:
return el->style->marginTop + el->style->borderTop + el->style->paddingTop
+ el->style->paddingBottom + el->style->borderBottom + el->style->marginBottom;
}
return 0;
}

View File

@ -119,3 +119,21 @@ void utils_array_shift(array_t *arr, size_t by)
arr->capacity = new->capacity;
free(new);
}
void utils_array_invert(array_t *arr)
{
size_t pivot, i;
void *left, *right;
if (!arr)
{
return;
}
pivot = arr->capacity / 2;
for (i = 0; i < pivot; i++)
{
left = arr->list[i];
right = arr->list[arr->capacity - i - 1];
arr->list[i] = right;
arr->list[arr->capacity - i - 1] = left;
}
}

View File

@ -1,198 +0,0 @@
/*#include <utils.h>*/
#include <gint/display.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#define PI 3.14152
/* Converts a value to a blurhash */
uint8_t table[256] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,62,63,64,0,0,0,0,65,66,67,68,69,0,0,1,2,3,4,5,6,7,8,9,70,71,0,72,0,73,74,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,75,0,76,77,78,0,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,79,80,81,82,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
/* Reads a blurhash number */
static uint32_t read_number(char *str, size_t n)
{
uint32_t v = 0;
size_t i;
for (i = 0; i < n; i++)
{
char c = str[i];
if (!c)
{
break;
}
v += table[c];
v *= 83;
}
v /= 83;
return v;
}
uint16_t gco(char *coeff, int i, int j, int nx, int ny)
{
size_t offset;
uint16_t v;
if (i == 0 && j == 0)
{
/* It isn't my job to manage the DC */
return 0;
}
offset = ((nx * j + i) * 2) + 2;
v = read_number(coeff + offset, 2);
return v;
}
float ch_to_float(uint8_t coeff, uint8_t max)
{
int s = coeff < 9 ? -1 : 1;
float max_float = (max + 1) / 166.0;
float coeff_float = s * powf((coeff - 9) / 9.0, 2);
return coeff_float * max_float;
}
typedef struct rgb {
float r, g, b;
} rgb_t;
rgb_t ac_coeff_to_color(uint16_t coeff, uint8_t max)
{
uint8_t r, g, b;
float fr, fg, fb;
r = (coeff / (19*19)) % 19;
g = (coeff / 19 ) % 19;
b = (coeff ) % 19;
fr = ch_to_float(r, max);
fg = ch_to_float(g, max);
fb = ch_to_float(b, max);
return (rgb_t) { .r = fr, .g = fg, .b = fb };
}
/* stolen lol */
static inline float to_linear(int value)
{
float v = (float)value / 255;
if(v <= 0.04045) return v / 12.92;
else return powf((v + 0.055) / 1.055, 2.4);
}
static inline int to_srgb(float value)
{
float v = fmaxf(0, fminf(1, value));
if(v <= 0.0031308) return v * 12.92 * 255 + 0.5;
else return (1.055 * powf(v, 1 / 2.4) - 0.055) * 255 + 0.5;
}
rgb_t dc_coeff_to_color(uint32_t srgb, uint8_t max)
{
float fr, fg, fb;
fr = to_linear(((srgb >> (8 * 2)) & 0xFF));
fg = to_linear(((srgb >> (8 * 1)) & 0xFF));
fb = to_linear(((srgb >> (8 * 0)) & 0xFF));
return (rgb_t) { .r = fr, .g = fg, .b = fb };
}
uint16_t *utils_decode_blurhash(char *str, int *ow, int *oh)
{
uint16_t *buf;
rgb_t *coeffs;
rgb_t *colors;
uint8_t component;
uint8_t nx, ny;
uint8_t max_ac;
size_t x, y;
size_t i, j;
if (!str)
{
return NULL;
}
component = read_number(str, 1);
ny = (component / 9) + 1;
nx = (component % 9) + 1;
if (*ow == 0) *ow = nx;
if (*oh == 0) *oh = nx;
str += 1;
max_ac = read_number(str, 1);
str += 1;
/* Setup coefficients */
coeffs = malloc(nx * ny * sizeof(rgb_t));
for (j = 0; j < ny; j++)
{
for (i = 0; i < nx; i++)
{
uint16_t c;
if (i == 0 && j == 0)
{
/* DC coeff */
coeffs[0] = dc_coeff_to_color(read_number(str, 4), max_ac);
continue;
}
c = gco(str, i, j, nx, ny);
coeffs[j * nx + i] = ac_coeff_to_color(c, max_ac);
}
}
/* Now, it's math time. */
colors = malloc((*ow) * (*oh) * sizeof(rgb_t));
for (y = 0; y < *oh; y++)
{
for (x = 0; x < *ow; x++)
{
size_t i, j;
rgb_t rgb = { .r = 0., .g = 0., .b = 0. };
/* Comical amount of nesting */
for (j = 0; j < ny; j++)
{
for (i = 0; i < nx; i++)
{
rgb_t cij = coeffs[j * nx + i];
float k = cosf(((float)x * i * PI)/ *ow) * cosf((y * j * PI)/ *oh);
rgb.r += cij.r * k;
rgb.g += cij.g * k;
rgb.b += cij.b * k;
}
}
colors[y * *ow + x] = rgb;
}
}
buf = malloc(nx * ny * sizeof(uint16_t));
/* WRITE to buf */
for (y = 0; y < *oh; y++)
{
for (x = 0; x < *ow; x++)
{
rgb_t c = colors[y * *ow + x];
uint8_t cr, cg, cb;
cr = to_srgb(c.r);
cg = to_srgb(c.g);
cb = to_srgb(c.b);
buf[y * *ow + x] = C_RGB(
(31 * cr)/255,
(31 * cg)/255,
(31 * cb)/255
);
}
}
free(coeffs);
free(colors);
return buf;
}

View File

@ -4,10 +4,11 @@
#include <string.h>
#include <utils.h>
#include <log.h>
command_t *command_parse(char *str)
{
command_t *ret;
command_t *ret = NULL;
char *name, *argstart;
if (!str)
@ -19,7 +20,6 @@ command_t *command_parse(char *str)
return NULL;
}
/* TODO */
str++;
argstart = str;
while (*str != ' ' && *str)
@ -29,19 +29,37 @@ command_t *command_parse(char *str)
if (argstart == str)
{
/* Empty name, give up. */
log_text("command failure because empty...");
return NULL;
}
name = malloc((size_t) (str - argstart) + 1);
memcpy(name, argstart, (size_t) (str - argstart));
name[(size_t) (str - argstart)] = '\0';
ret = malloc(sizeof(command_t));
ret->command = name;
ret->arguments = utils_new_array();
while (*str)
{
size_t length;
char *substr;
/* TODO: string args that allow spaces */
while (*str == ' ' && *str)
{
str++;
}
argstart = str;
while (*str != ' ' && *str)
{
str++;
}
length = (size_t)(str - argstart)/sizeof(char);
substr = malloc((length + 2) * sizeof(char));
memcpy(substr, argstart, length);
substr[length] = '\0';
utils_array_add(ret->arguments, substr);
}
return ret;

View File

@ -11,15 +11,14 @@
#include <gint/usb.h>
#include <string.h>
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <info.h>
volatile static uintptr_t *stacktrace = NULL;
volatile static size_t idx = 0;
volatile static size_t allocated = 0;
static volatile uintptr_t *stacktrace = NULL;
static volatile size_t idx = 0;
static volatile size_t allocated = 0;
static const char *dbg_to_str(uint32_t code)
{
@ -89,8 +88,18 @@ static void __attribute__((noreturn,no_instrument_function)) dbg_panic(uint32_t
"dbg", "crash",
idx * sizeof(uintptr_t)
);
usb_write_sync(out, &header, sizeof(usb_fxlink_header_t), false);
usb_write_sync(out, stacktrace, idx * sizeof(uintptr_t), false);
usb_write_sync(
out,
&header,
sizeof(usb_fxlink_header_t),
false
);
usb_write_sync(
out,
(void *) stacktrace,
idx * sizeof(uintptr_t),
false
);
usb_commit_sync(out);
/* We aren't going to wait for anything here. */
}
@ -136,6 +145,7 @@ void __attribute__((no_instrument_function)) __cyg_profile_func_enter(void *this
return;
}
stacktrace[idx++] = (uintptr_t) this;
(void) callee;
}
void __attribute__((no_instrument_function)) __cyg_profile_func_exit(void *this, void *callee)
{
@ -148,5 +158,7 @@ void __attribute__((no_instrument_function)) __cyg_profile_func_exit(void *this,
return;
}
stacktrace[--idx] = (uintptr_t) NULL;
(void) this;
(void) callee;
}

208
src/dom.c Normal file
View File

@ -0,0 +1,208 @@
#include "dom.h"
#include <stdlib.h>
#include "utils.h"
#include "yxml.h"
#define YXML_BUFFER 4096
static html_elem_t *create_element(char *name, html_elem_t *parent);
static html_elem_t *create_text(char *text, html_elem_t *parent);
static void add_attr(html_elem_t *e, char *key, char *value);
static void concat_str(char **initial, char *add);
html_elem_t *dom_parse_element(char *buf)
{
#define push(e) utils_array_add(stack, e)
#define peek() utils_array_get(stack, utils_array_size(stack) - 1)
#define pop() utils_array_pop(stack)
yxml_t *x = NULL;
html_elem_t *ret = NULL;
html_elem_t *top = NULL;
char *str_attr = NULL;
char *str_cntr = NULL;
array_t *stack;
if (!buf)
{
return NULL;
}
x = malloc(sizeof(*x) + YXML_BUFFER);
yxml_init(x, &x[1], YXML_BUFFER);
stack = utils_new_array();
for (; *buf; buf++)
{
yxml_ret_t r = yxml_parse(x, *buf);
if (r < 0)
{
goto cleanup;
}
if (r == 0)
{
continue;
}
/* We have a token, check how to deal with it */
switch (r)
{
case YXML_ELEMSTART:
if (str_cntr)
{
/* We have to add a text element to our top */
create_text(str_cntr, peek());
free(str_cntr);
str_cntr = NULL;
}
/* Create a new element that will populated. */
top = create_element(x->elem, peek());
if (!ret)
{
/* If we didn't have any element before, this one is
* going to be our top of the stack */
ret = top;
}
push(top);
/* Fallthrough */
case YXML_ATTRSTART:
if (str_attr)
{
free(str_attr);
str_attr = NULL;
}
break;
case YXML_ATTRVAL:
concat_str(&str_attr, x->data);
break;
case YXML_ATTREND:
add_attr(top, x->attr, str_attr);
free(str_attr);
str_attr = NULL;
break;
case YXML_CONTENT:
concat_str(&str_cntr, x->data);
break;
case YXML_ELEMEND:
/* Pop out an element out of the DOM. */
if (str_cntr)
{
/* We have to add a text element to our top */
create_text(str_cntr, peek());
free(str_cntr);
str_cntr = NULL;
}
pop();
break;
default: break;
}
}
free(x);
utils_free_array(stack);
return ret;
cleanup:
if (x)
{
free(x);
}
if (ret)
{
dom_free_element(ret);
}
return NULL;
}
void dom_free_element(html_elem_t *e)
{
char *key, *val;
if (!e)
{
return;
}
free(e->name);
while (utils_hashmap_list(e->attrs, &key, (void **) &val))
{
free(val);
}
utils_free_hashmap(e->attrs);
if (e->has_text)
{
free(e->children.text);
}
else
{
size_t i, children;
children = utils_array_size(e->children.elems);
for (i = 0; i < children; i++)
{
html_elem_t *c = utils_array_get(e->children.elems, i);
dom_free_element(c);
}
utils_free_array(e->children.elems);
}
free(e);
}
static html_elem_t *create_text(char *text, html_elem_t *parent)
{
html_elem_t *ret;
if (!text)
{
return NULL;
}
ret = create_element("[<text>]", parent);
utils_free_array(ret->children.elems);
ret->has_text = true;
ret->children.text = utils_strcpy(text);
return ret;
}
static html_elem_t *create_element(char *name, html_elem_t *parent)
{
html_elem_t *ret;
if (!name)
{
return NULL;
}
ret = malloc(sizeof(*ret));
ret->name = utils_strcpy(name);
ret->children.elems = utils_new_array();
ret->has_text = false;
ret->attrs = utils_new_hashmap();
ret->parent = parent;
if (parent && !parent->has_text)
{
utils_array_add(parent->children.elems, ret);
}
return ret;
}
static void add_attr(html_elem_t *e, char *key, char *value)
{
if (!e || !key || !value)
{
return;
}
utils_hashmap_add(e->attrs, key, utils_strcpy(value));
}
static void concat_str(char **initial, char *add)
{
char *new;
if (!initial || !add)
{
return;
}
if (!*initial)
{
*initial = utils_strcpy(add);
return;
}
new = utils_strcat(*initial, add);
free(*initial);
*initial = new;
}

View File

@ -13,8 +13,8 @@ void easter_dvd_frame(const char *str)
if (x == -100 && y == -100)
{
x = rand() % DWIDTH;
y = rand() % DHEIGHT;
x = rand() % (DWIDTH - w);
y = rand() % (DHEIGHT - h);
}
x += dx;

View File

@ -1,3 +1,4 @@
#include "http.h"
#include <matrix.h>
#include <justui/jlabel.h>
@ -72,6 +73,7 @@ extern m_event_t *matrix_parse_event(hashmap_t *json, bool strict)
event->sender = sender;
event->state_key = state_key;
event->type = type;
event->redacted = false;
return event;
fail:
@ -105,3 +107,79 @@ void matrix_free_event(m_event_t *event)
end_val(type, free);
#undef end_val
}
hashmap_t *matrix_event_to_json(m_user_t *user, m_event_t *event)
{
http_transfer_t *trans;
hashmap_t *ret;
char *path;
if (!event || !user)
{
return NULL;
}
path = utils_sprintf(
"/_matrix/client/v3/rooms/%s/event/%s",
event->room_id, event->event_id
);
trans = http_transfer_create(HTTP_GET, user->server, path);
matrix_set_token(trans, user);
free(path);
http_transfer_send(trans);
if (http_transfer_code(trans) != 200)
{
http_transfer_free(trans);
return NULL;
}
ret = http_get_reply_json(trans);
http_transfer_free(trans);
return ret;
}
json_value_t *matrix_get_relation_property(m_event_t *e, char *k)
{
json_value_t *rel_v;
hashmap_t *rel;
if (!e || !k)
{
return NULL;
}
rel_v = utils_hashmap_get(e->content, "m.relates_to");
if (!rel_v)
{
return NULL;
}
rel = utils_json_as_object(rel_v);
return utils_hashmap_get(rel, k);
}
char *matrix_get_relation_type(m_event_t *event)
{
return utils_json_as_string(
matrix_get_relation_property(event, "rel_type")
);
}
char *matrix_get_relation_source(m_event_t *event)
{
return utils_json_as_string(
matrix_get_relation_property(event, "event_id")
);
}
void matrix_add_relation(char *id, m_room_t *room, m_event_t *event)
{
array_t *relations;
if (!id || !room || !event)
{
return;
}
relations = utils_hashmap_get(room->relations, id);
if (!relations)
{
relations = utils_new_array();
utils_hashmap_add(room->relations, id, relations);
}
log_text("adding relation to event %s", id);
utils_array_add(relations, event);
}

View File

@ -1,31 +1,40 @@
#include <matrix.h>
#include <string.h>
#include <stdlib.h>
#include <justui/jlabel.h>
#include <utils.h>
#include <log.h>
#include <ui.h>
#include <WebCalc.h>
#include <DOM.h>
#include <dom.h>
#define GRAY C_RGB(20, 20, 20)
extern bopti_image_t fileicon;
extern font_t terminus_weak;
extern font_t terminus_strong;
extern font_t uf8x9;
extern font_t uf5x7;
typedef struct dom_state {
bool italic, bold;
int bg, fg;
m_event_t *event;
m_room_t *room;
} dom_state_t;
static void *render_span(void *p, struct DomElement *dom, dom_state_t state)
static void *render_text(void *p, html_elem_t *elem, dom_state_t state)
{
jlabel *label;
if (!elem->has_text)
{
return NULL;
}
label = jlabel_create("", p);
jlabel_asprintf(label, "%s", dom->textNode);
jlabel_asprintf(label, "%s", elem->children.text);
jlabel_set_font(label, &uf5x7);
if (state.italic)
{
jlabel_set_font(label, &terminus_weak);
@ -44,26 +53,30 @@ static void *render_span(void *p, struct DomElement *dom, dom_state_t state)
}
return label;
}
static jwidget *parse_msg_body_dom(void *p, struct DomElement *dom, dom_state_t s)
static jwidget *parse_msg_body_dom(void *p, html_elem_t *elem, dom_state_t s)
{
size_t i;
size_t i, children;
jwidget *container = jwidget_create(p);
jwidget *line;
jwidget_set_stretch(container, 1, 0, false);
jlayout_set_vbox(container)->spacing = 1;
jwidget_set_stretch(container, 1, 0, false);
line = jwidget_create(container);
jlayout_set_hbox(line)->spacing = 1;
jwidget_set_stretch(line, 1, 0, false);
jlayout_set_hbox(line)->spacing = 0;
jwidget_set_stretch(line, 1, 1, false);
for (i = 0; i < dom->childnum; i++)
children = utils_array_size(elem->children.elems);
for (i = 0; i < children; i++)
{
struct DomElement *item = dom->children[i];
if (!strcmp(item->tagName, "span"))
html_elem_t *item = utils_array_get(elem->children.elems, i);
bool has_reply = false;
has_reply = !!matrix_get_relation_property(s.event, "m.in_reply_to");
if (item->has_text)
{
render_span(line, item, s);
render_text(line, item, s);
}
else if (!strcmp(item->tagName, "u"))
else if (!strcmp(item->name, "u"))
{
jwidget *box = jwidget_create(line);
jwidget *ul;
@ -75,19 +88,18 @@ static jwidget *parse_msg_body_dom(void *p, struct DomElement *dom, dom_state_t
jwidget_set_stretch(ul, 1, 0, false); /* TODO: Make ul *work* */
jwidget_set_stretch(l, 1, 0, false);
jwidget_set_minimum_width(ul, l->w);
log_text("%s: size=%d", __func__, l->w);
jwidget_set_minimum_height(ul, 1);
jwidget_set_maximum_height(ul, 1);
jwidget_set_border(ul, J_BORDER_SOLID, 1, C_BLACK);
}
else if(!strcmp(item->tagName, "i") ||
!strcmp(item->tagName, "em"))
else if(!strcmp(item->name, "i") ||
!strcmp(item->name, "em"))
{
dom_state_t sc = s;
sc.italic = true;
parse_msg_body_dom(line, item, sc);
}
else if (!strcmp(item->tagName, "code"))
else if (!strcmp(item->name, "code"))
{
dom_state_t sc = s;
sc.italic = true;
@ -96,58 +108,79 @@ static jwidget *parse_msg_body_dom(void *p, struct DomElement *dom, dom_state_t
parse_msg_body_dom(line, item, sc);
}
else if (!strcmp(item->tagName, "strong"))
else if (!strcmp(item->name, "strong"))
{
dom_state_t sc = s;
sc.bold = true;
parse_msg_body_dom(line, item, sc);
}
else if (!strcmp(item->tagName, "p"))
else if (!strcmp(item->name, "p"))
{
parse_msg_body_dom(line, item, s);
line = jwidget_create(container);
jlayout_set_hbox(line)->spacing = 1;
jlayout_set_vbox(line)->spacing = 0;
}
else if (!strcmp(item->tagName, "h1"))
else if (!strcmp(item->name, "h1"))
{
void *w;
dom_state_t sc = s;
sc.bold = true;
sc.fg = GRAY;
w = parse_msg_body_dom(line, item, sc);
parse_msg_body_dom(line, item, sc);
line = jwidget_create(container);
jlayout_set_hbox(line)->spacing = 1;
jwidget_set_stretch(line, 1, 0, false);
jlayout_set_vbox(line)->spacing = 0;
jwidget_set_stretch(line, 1, 1, false);
}
else if (!strcmp(item->name, "a"))
{
dom_state_t sc = s;
sc.fg = C_RGB(4, 17, 26);
sc.bg = GRAY;
parse_msg_body_dom(line, item, sc);
}
else if (!strcmp(item->name, "blockquote"))
{
dom_state_t sc = s;
sc.italic = true;
sc.fg = GRAY;
sc.bg = C_NONE;
parse_msg_body_dom(line, item, sc);
line = jwidget_create(container);
jlayout_set_vbox(line)->spacing = 0;
jwidget_set_stretch(line, 1, 1, false);
}
else if (!strcmp(item->name, "br"))
{
line = jwidget_create(container);
jlayout_set_vbox(line)->spacing = 0;
jwidget_set_stretch(line, 1, 1, false);
parse_msg_body_dom(line, item, s);
}
else if (!strcmp(item->tagName, "br"))
else if (!strcmp(item->name, "mx-reply") && has_reply)
{
size_t j;
line = jwidget_create(container);
jlayout_set_hbox(line)->spacing = 1;
jwidget_set_stretch(line, 1, 0, false);
parse_msg_body_dom(line, item, s);
/* Do nothing. TODO: Render in separate. */
}
else
{
return NULL;
/* Try your best rendering. */
parse_msg_body_dom(line, item, s);
}
}
return container;
}
static jwidget *parse_msg_body_html(void *p, char *html)
static jwidget *parse_msg_body_html(void *p, char *html, m_room_t *r, m_event_t *e)
{
struct el_t *top;
struct DomElement *dom;
html_elem_t *elem;
char *contained, *cpy;
jwidget *container;
size_t i;
dom_state_t def = {
.bg = C_NONE, .fg = C_NONE,
.bold = false, .italic = false
.bold = false, .italic = false,
.room = r, .event = e
};
contained = utils_strcat("<html>", html);
@ -155,25 +188,24 @@ static jwidget *parse_msg_body_html(void *p, char *html)
contained = utils_strcat(contained, "</html>");
free(cpy);
html_parse(contained, contained + strlen(contained));
top = html_elem();
if (!top)
elem = dom_parse_element(contained);
if (!elem)
{
free(contained);
return NULL;
}
dom = top->dom;
/* Actually manage the HTML */
container = parse_msg_body_dom(p, dom, def);
container = parse_msg_body_dom(p, elem, def);
if (!container)
{
free(contained);
html_clear();
dom_free_element(elem);
return NULL;
}
free(contained);
html_clear();
dom_free_element(elem);
return container;
}
@ -184,15 +216,60 @@ static jwidget *parse_msg_body_plain(void *p, char *plain)
jwidget_set_stretch(container, 1, 0, false);
label = jlabel_create("", container);
jlabel_set_font(label, &uf5x7);
jlabel_asprintf(label, "%s", plain);
return container;
}
static jwidget *parse_msg_body(hashmap_t *body, void *p)
static jwidget *parse_msg_body(hashmap_t *body, void *p, m_room_t *r, m_event_t *e, m_user_t *u)
{
char *formatted_as = utils_json_as_string(
utils_hashmap_get(body, "format")
);
json_value_t *reply_val = matrix_get_relation_property(e, "m.in_reply_to");
if (reply_val)
{
hashmap_t *reply_obj = utils_json_as_object(reply_val);
json_value_t *id = utils_hashmap_get(reply_obj, "event_id");
char *id_str = utils_json_as_string(id);
m_event_t *reply_event = utils_array_get(
r->timeline, is_in_timeline(r, id_str)
);
uint16_t color = reply_event ? matrix_hash(reply_event->sender) : 0;
if (color)
{
jwidget *bar_and_reply = jwidget_create(p);
jwidget *reply_and_content = jwidget_create(bar_and_reply);
jwidget *b_reply = jwidget_create(reply_and_content);
jwidget *bar = jwidget_create(b_reply);
jwidget *reply = jwidget_create(b_reply);
p = jwidget_create(reply_and_content);
jlayout_set_hbox(bar_and_reply)->spacing = 1;
jwidget_set_stretch(bar_and_reply, 1, 0, false);
jlayout_set_hbox(b_reply)->spacing = 1;
jwidget_set_stretch(b_reply, 1, 0, false);
jwidget_set_fixed_width(bar, 5);
jwidget_set_minimum_height(bar, 30);
jwidget_set_stretch(bar, 1, 1, false);
jwidget_set_background(bar, color);
jwidget_set_border(bar, J_BORDER_SOLID, 1, GRAY);
jlayout_set_vbox(reply_and_content)->spacing = 0;
jwidget_set_stretch(reply_and_content, 1, 0, false);
jwidget_set_stretch(reply, 1, 0, false);
jwidget_set_stretch(p, 1, 0, false);
matrix_show_event(r, u, reply_event, reply);
}
}
if (!formatted_as || strcmp(formatted_as, "org.matrix.custom.html"))
{
@ -206,7 +283,7 @@ static jwidget *parse_msg_body(hashmap_t *body, void *p)
jwidget *w = parse_msg_body_html(p,
utils_json_as_string(
utils_hashmap_get(body, "formatted_body")
)
), r, e
);
if (!w)
{
@ -226,9 +303,26 @@ static void show_msg(m_room_t *r, m_user_t *u, m_event_t *e, jwidget *w)
utils_hashmap_get(e->content, "msgtype")
);
if (e->redacted)
{
/* Don't even try. */
jlabel *content;
content = jlabel_create("[Redacted]", w);
jlabel_set_text_color(content, GRAY);
jwidget_set_stretch(content, 1, 0, false);
return;
}
if (!strcmp(subtype, "m.text"))
{
parse_msg_body(e->content, w);
parse_msg_body(e->content, w, r, e, u);
return;
}
if (!strcmp(subtype, "m.notice"))
{
/* TODO: Indicate that the message comes from a brobot */
parse_msg_body(e->content, w, r, e, u);
return;
}
if (!strcmp(subtype, "m.emote"))
@ -250,18 +344,31 @@ static void show_msg(m_room_t *r, m_user_t *u, m_event_t *e, jwidget *w)
);
}
jwidget_set_stretch(content, 1, 0, false);
return;
}
if (!strcmp(subtype, "m.image"))
{
json_value_t *url_val;
jmimage *img;
url_val = utils_hashmap_get(e->content, "url");
img = matrix_image_from_mxc(
w, u,
utils_json_as_string(url_val), 96
);
jwidget_set_stretch(img, 1, 0, false);
return;
}
if (!strcmp(subtype, "m.file"))
{
char *body;
jwidget *contentf;
jpainted *fi;
jlabel *content;
contentf = jwidget_create(w);
jwidget_set_stretch(contentf, 1, 0, false);
jlayout_set_hbox(contentf)->spacing = 2;
fi = ui_image(contentf, &fileicon);
ui_image(contentf, &fileicon);
body = utils_json_as_string(utils_hashmap_get(e->content, "body"));
content = jlabel_create("", contentf);
@ -283,12 +390,104 @@ static void show_msg(m_room_t *r, m_user_t *u, m_event_t *e, jwidget *w)
jwidget_set_stretch(content, 1, 0, false);
}
}
extern img_t default_pfp;
#define default_msg_head() \
img = matrix_image_from_room_user(\
content_box, \
u, \
utils_hashmap_get(u->rooms, e->room_id),\
e->sender\
);\
if (!img)\
{\
img = jmimage_create(content_box, default_pfp);\
}\
wbox = jwidget_create(content_box);\
jwidget_set_stretch(wbox, 1, 0, false);\
jlayout_set_vbox(wbox)->spacing = 1;\
nick = matrix_resolve_nick(u, e->sender, r);\
sender = jlabel_create("", wbox);\
jlabel_set_font(sender, &uf5x7);\
jlabel_asprintf(sender, "%s", nick);\
jlabel_set_text_color(sender, matrix_hash(e->sender));\
jwidget_set_stretch(sender, 1, 0, false);\
content = jwidget_create(wbox);\
jwidget_set_stretch(content, 1, 0, false);\
jlayout_set_vbox(content)->spacing = 1
void show_member(m_room_t *r, m_user_t *u, m_event_t *e, jwidget *content_box)
{
char *membership = utils_json_as_string(
utils_hashmap_get(e->content, "membership")
);
char *name = utils_json_as_string(
utils_hashmap_get(e->content, "displayname")
);
char *reason = utils_json_as_string(
utils_hashmap_get(e->content, "reason")
);
jlabel *label;
if (!name)
{
name = e->state_key;
}
label = jlabel_create("", content_box);
jwidget_set_stretch(label, 1, 0, false);
jlabel_set_font(label, &uf8x9);
jlabel_set_text_color(label, GRAY);
if (!strcmp(membership, "join"))
{
jlabel_asprintf(label, "%s joined.", name);
}
if (!strcmp(membership, "leave"))
{
if (!reason)
{
jlabel_asprintf(label, "%s left.", name);
return;
}
jlabel_asprintf(label, "%s left: %s", name, reason);
}
if (!strcmp(membership, "invite"))
{
char *who;
if (!(who = matrix_resolve_nick(u, e->sender, r)))
{
who = e->sender;
}
jlabel_asprintf(label, "%s was invited by %s.", name, who);
}
if (!strcmp(membership, "ban"))
{
char *who;
if (!(who = matrix_resolve_nick(u, e->sender, r)))
{
who = e->sender;
}
if (!reason)
{
jlabel_asprintf(
label,
"%s was banned by %s.", name, who
);
return;
}
jlabel_asprintf(
label,
"%s was banned by %s: %s", name, who, reason
);
}
}
void matrix_show_event(m_room_t *r, m_user_t *u, m_event_t *e, jwidget *w)
{
char *event_type;
char *nick;
jwidget *wbox;
jwidget *content;
jwidget *content_box;
jmimage *img;
jlabel *sender;
if (!r || !u || !e || !w)
{
@ -296,26 +495,92 @@ void matrix_show_event(m_room_t *r, m_user_t *u, m_event_t *e, jwidget *w)
}
jlayout_set_vbox(w)->spacing = 2;
jwidget_set_stretch(w, 1, 0, false);
jlayout_set_vbox(w)->spacing = 1;
content_box = jwidget_create(w);
jwidget_set_stretch(content_box, 1, 0, false);
jlayout_set_hbox(content_box)->spacing = 1;
/* Set nickname properly */
nick = matrix_resolve_nick(u, e->sender, r);
sender = jlabel_create("", w);
jlabel_asprintf(sender, "%s", nick);
jlabel_set_text_color(sender, matrix_hash(e->sender));
jwidget_set_stretch(sender, 1, 0, false);
content = jwidget_create(w);
jwidget_set_stretch(content, 1, 0, false);
jlayout_set_vbox(content)->spacing = 1;
event_type = e->type;
if (!strcmp(event_type, "m.room.message"))
{
default_msg_head();
show_msg(r, u, e, content);
return;
}
if (!strcmp(event_type, "m.sticker"))
{
json_value_t *url_val;
jmimage *img;
default_msg_head();
url_val = utils_hashmap_get(e->content, "url");
img = matrix_image_from_mxc(
content,
u,
utils_json_as_string(url_val),
32
);
jwidget_set_stretch(img, 1, 0, false);
}
if (!strcmp(event_type, "m.room.member"))
{
show_member(r, u, e, content_box);
}
if (!strcmp(event_type, "m.reaction"))
{
jlabel *label;
char *key = utils_json_as_string(
matrix_get_relation_property(e, "key")
);
char *nick = matrix_resolve_nick(u, e->sender, r);
if (!nick)
{
nick = e->sender;
}
label = jlabel_create("", content_box);
jlabel_asprintf(label, "%s reacted with '%s'", nick, key);
if (!key)
{
jlabel_asprintf(label, "[Redacted]");
}
jwidget_set_stretch(label, 1, 0, false);
jlabel_set_font(label, &uf8x9);
jlabel_set_text_color(label, GRAY);
}
{
array_t *relations = utils_hashmap_get(r->relations, e->event_id);
size_t i;
jwidget *reactions;
if (!relations)
{
return;
}
reactions = jwidget_create(w);
jwidget_set_stretch(reactions, 1, 0, false);
jlayout_set_hbox(reactions)->spacing = 3;
for (i = 0; i < utils_array_size(relations); i++)
{
m_event_t *child = utils_array_get(relations, i);
char *rel_type = matrix_get_relation_type(child);
if (!strcmp(rel_type, "m.annotation"))
{
jlabel *label = jlabel_create("", reactions);
char *key = utils_json_as_string(
matrix_get_relation_property(child, "key")
);
jlabel_asprintf(label, "[%s]", key);
jlabel_set_font(label, &uf5x7);
}
}
}
/* TODO: Other message types */
}

View File

@ -1,18 +1,26 @@
#include <utils.h>
#include <gint/gint.h>
#include <gint/fs.h>
#include <stdlib.h>
#include <string.h>
#include <usb.h>
static volatile char *data;
static volatile size_t len;
static volatile bool fine;
static void os_get(char *path)
{
FILE *f = fopen(path, "r");
FILE *f = fopen(path, "rb");
if (!f)
{
fine = false;
return;
}
fseek(f, 0L, SEEK_END);
len = ftell(f);
rewind(f);
@ -27,12 +35,125 @@ char * utils_get_file(char *path, size_t *l)
int null;
usb_close();
fine = true;
gint_world_switch(GINT_CALL(os_get, path));
*l = len;
usb_init(&null, &null);
if (!fine)
{
return NULL;
}
(void) null;
return (char *) data;
}
static void os_put(char *path, char *data, uint32_t l)
{
FILE *f = fopen(path, "wb");
fwrite(data, l, 1, f);
fclose(f);
}
void utils_write_file(char *path, char *data, size_t l)
{
int null;
if (!path || !data || l == 0)
{
return;
}
usb_close();
gint_world_switch(GINT_CALL(os_put, path, data, (uint32_t) l));
usb_init(&null, &null);
(void) null;
}
void utils_delete_file(char *path)
{
int null;
if (!path)
{
return;
}
usb_close();
gint_world_switch(GINT_CALL(remove, path));
usb_init(&null, &null);
(void) null;
}
typedef struct memory_info {
void *buffer;
size_t size;
off_t pos;
} memory_info_t;
static ssize_t r(void *data, void *buf, size_t size)
{
memory_info_t *info = data;
ssize_t left = ((ssize_t) (info->size)) - ((ssize_t) info->pos);
ssize_t min = left < (ssize_t) size ? left : (ssize_t) size;
if (left <= 0)
{
return -1;
}
memcpy(buf, (char *) (info->buffer) + info->pos, min);
info->pos += min;
return min;
}
off_t s(void *data, off_t offset, int whence)
{
memory_info_t *info = data;
if (whence == SEEK_SET)
{
info->pos = offset;
}
if (whence == SEEK_CUR)
{
info->pos += offset;
}
if (whence == SEEK_END)
{
info->pos = info->size + offset;
}
return info->pos;
}
int c(void *data)
{
free(data);
return 0;
}
fs_descriptor_type_t mem_type = {
.read = r,
.write = NULL,
.lseek = s,
.close = c,
};
int utils_openmem(void *buf, size_t size)
{
memory_info_t *info;
if (!buf || !size)
{
return -1;
}
info = malloc(sizeof(memory_info_t));
info->buffer = buf;
info->size = size;
info->pos = 0;
return open_generic(&mem_type, info, -1);
}
FILE *utils_fopenmem(void *buf, size_t size)
{
/* You can't write there. */
return fdopen(utils_openmem(buf, size), "rb");
}

View File

@ -28,6 +28,8 @@ __attribute((noreturn)) void goda_panic(uint32_t code)
dtext(50, 61, C_WHITE, "How do you even fucking find this any funny?");
dupdate();
(void) code;
while (true);
}
@ -38,7 +40,7 @@ void easter_goda(void)
img_t goda_decal;
size_t i;
size_t ex, ey;
int x;
size_t x;
timeout_t tm;
const char *godatxt =
@ -114,7 +116,7 @@ void easter_goda(void)
}
dupdate();
x++;
if (x >= (godatxtw + 60))
if (x >= (size_t) (godatxtw + 60))
{
x = -DWIDTH;
i++;

View File

@ -30,7 +30,7 @@ static size_t string_hash(char *str)
size_t hash = 5381;
size_t i;
for (i = 0; i < strlen(str); i++)
for (i = 0; i < length; i++)
{
hash = (hash << 5) + hash + str[i];
}
@ -105,7 +105,7 @@ void * utils_hashmap_get(hashmap_t *hm, char *key)
size_t hash, probing;
size_t lhsh;
bool found = false;
if (!hm || !key || hm->on_it)
{
@ -135,8 +135,6 @@ void * utils_hashmap_set(hashmap_t *hm, char *key, void *value)
void *prev = NULL;
size_t i;
if (!hm || !key || !value)
{
return NULL;
@ -170,8 +168,6 @@ void * utils_hashmap_del(hashmap_t *hm, char *key)
size_t hash, probing;
size_t lhsh;
bool found = false;
if (!hm || !key || !hm->on_it)
{
return NULL;
@ -246,5 +242,6 @@ bool utils_hashmap_list(hashmap_t *hm, char **kp, void **vp)
}
size_t utils_hashmap_elems(hashmap_t *hm)
{
(void) hm;
return 0;
}

View File

@ -1,456 +0,0 @@
#include <WebCalc.h>
#include <log.h>
int isvalid(int c)
{
return (c>='0' && c<='9') || (c>='A' && c<='Z') || (c>='a' && c<='z')
|| c=='-';
}
#define isspecs(c) ((c)=='/' || (c)=='!')
#define isspace(c) ((c)==' ' || (c)=='\t' || (c)=='\n')
// Block type enumeration definition.
enum block_t
{
block_tag = 0,
block_text = 1
};
static struct el_t *top = NULL;
static struct el_t *tiff = NULL;
struct el_t *html_elem(void)
{
return tiff;
}
/**
* html_text()
*
* Indicates if the given tag name is associated with a text content, or
* not.
*
* @param name Reference on tag name.
*
* @return 1 if the tag is supposed to have a text content, 0
* otherwise.
*/
static int html_text(reference_t name)
{
const char *texts[] = { "p", "h1", "h2", "h3", "h4", "h5", "h6",
"math", "center", "right", NULL };
const char **ptr = texts;
/*while(*ptr) if(!refscmp(name,*ptr++)) return 1;*/
return 0;
}
static int html_conf(reference_t name)
{
const char *confs[] = { "head", "link", NULL };
const char **ptr = confs;
while(*ptr) if(!refscmp(name,*ptr++)) return 1;
return 0;
}
static void html_configure(reference_t name, reference_t args)
{
html_args(&args, NULL);
reference_t arg, value;
/* Let's not add CSS like that. */
/*if(!refscmp(name,"link"))
{
reference_t rel = { 0,0 }, href = { 0,0 };
while(1)
{
html_args(&arg, &value);
if(refnull(arg)) break;
else if(!refscmp(arg, "rel")) rel = value;
else if(!refscmp(arg, "href")) href = value;
}
if(refnull(rel) || refnull(href)) return;
if(!refscmp(rel, "stylesheet")) Window_addStylesheet(href);
}*/
}
static void html_create(struct el_t *el)
{
const char *ptr;
// Debugging the tag parameters.
fputs("{\n\t", stdout);
ptr = el->name.begin;
while(ptr < el->name.end) fputc(*ptr++, stdout);
if(!refnull(el->args))
{
fputs("\n\t", stdout);
ptr = el->args.begin;
while(ptr < el->args.end) fputc(*ptr++, stdout);
}
if(!refnull(el->text))
{
fputs("\n\t\"", stdout);
ptr = el->text.begin;
while(ptr < el->text.end) fputc(*ptr++, stdout);
fputc('"', stdout);
}
fputs("\n}\n\n", stdout);
struct el_t *parent = el->down;
if(parent)
{
el->dom = DomElement_new(el->name, el->args, parent->dom,
DomElement_getChild(parent->dom, -1));
DomElement_addChild(parent->dom, el->dom);
}
else el->dom = DomElement_new(el->name, el->args, NULL, NULL);
if (!strncmp(el->name.begin, "html", 4))
{
tiff = el;
}
if(!refnull(el->text)) DomElement_setTextNode(el->dom, el->text);
/*if(!refscmp(el->name, "html")) Window_setHTML(el->dom);*/
}
/**
* html_event()
*
* Treats an event that concerns the given block with the given type. An
* event may lead to one of the following operations :
* - adding an element to the stack
* - removing an element from the stack, creating the dom object
* - setting parameters of the top object
*
* @param block Reference on the event block.
* @param type Type of the given block.
*
* @return Expected type for the next block.
*/
static enum block_t html_event(reference_t *b, enum block_t type)
{
// Using a temporary name and arguments references for validity tests.
reference_t name, args, block1, block;
// Using a common pointer and a common integer.
const char *ptr;
int x;
block = *b;
// Receiving an event for text content.
if(type == block_text)
{
// Setting the content reference.
if(top) top->text = block;
// Returning block_tag as nature of next expected element.
return block_tag;
}
// Only tag events reach this point.
// Extracting the tag name.
block1 = block;
while(!isvalid(*block.begin) && !isspecs(*block.begin) &&
block.begin < block.end) block.begin++;
if (block1.begin == block.begin)
{
size_t len;
block1.end = block1.begin;
while(*block1.end != '<') block1.end++;
len = block1.end - block1.begin;
/* Create a fake element(span) with our text */
struct el_t *el = (struct el_t *)malloc(sizeof(struct el_t));
el->down = top;
// Setting element parameters.
el->name.begin = "span";
el->name.end = el->name.begin + strlen(el->name.begin);
el->args.begin = "";
el->args.end = el->args.begin;
el->text = block1;
log_text("%s: Spanning", __func__);
b->end = block1.end;
// Creating non-textual tags to ensure stack order.
html_create(el);
/*top = top->down;*/
// Returning the correct expected data type.
return block_tag;
}
// Handling empty tags.
if(block.begin == block.end) return block_tag;
// Getting the name end.
ptr = block.begin + isspecs(*block.begin);
while(isvalid(*ptr)) ptr++;
// Setting name reference.
name.begin = block.begin;
name.end = ptr;
// Initializing tag validity checking operation.
x = 0;
ptr = name.begin + isspecs(*name.begin);
// Checking tag validity. (e.g. '<//tag>', '<! >'). x is validity.
while(ptr < name.end)
{
// Warning ! isvalid() is a macro, using *ptr++ would repeat
// pointer incrementation !
if(isvalid(*ptr)) x = 1;
// Incrementing pointer.
ptr++;
}
// A tag name is valid if it contains at least one valid character.
if(!x) return block_tag;
// Looking for a parameter string.
args.begin = name.end;
args.end = block.end - 1;
args = reftrim(args);
// Handling interpreter directives.
if(*name.begin == '!')
{
/* Log is disabled.
// Outputting a string.
fputc('#', output);
ptr = name.begin + 1;
while(ptr < name.end) fputc(*ptr++, output);
fputs(" ", output);
ptr = args.begin;
while(ptr < args.end) fputc(*ptr++, output);
fputs("\n\n", output);
*/
// Returning a proper data type.
return block_tag;
}
if(html_conf(name))
{
html_configure(name, args);
return block_tag;
}
// Handling closing tags.
if(*name.begin == '/')
{
name.begin++;
if(html_conf(name)) return block_tag;
struct el_t *el = top->down;
// Non-textual tags are created when opened.
if (el)
{
if(!top->dom) html_create(top);
free(top);
top = el; /* Wouldn't that be the opposite? */
}
return block_tag;
}
// Only opening tag events reach this point.
// Allocating an object.
struct el_t *el = (struct el_t *)malloc(sizeof(struct el_t));
el->down = top;
top = el;
// Setting element parameters.
log_text("%s: %s (%p-%p)", __func__, name, name.end, name.begin);
el->name = name;
el->args = args;
el->text.begin = el->text.end = NULL;
// Creating non-textual tags to ensure stack order.
if(!html_text(name)) html_create(el);
else el->dom = NULL;
// Returning the correct expected data type.
return html_text(name) ? block_text : block_tag;
}
/**
* html_next()
*
* Returns a reference on the next element of the given type in the given
* code, defined by a begin and an end pointer.
* An element may be empty, particularly when a text element (such as <p>)
* does not contain any text.
*
* @param begin Pointer on code.
* @param limit Pointer on the first byte that is outside the code.
* @param type Type of expected element.
*
* @return Reference on the element.
*/
static reference_t html_next(const char *begin, const char *limit,
enum block_t type)
{
// Using a reference.
reference_t ref = { begin, begin };
// Getting the next tag opening element.
while(ref.end < limit && *ref.end != '<') ref.end++;
// Returning if the given block type has the calculated limits.
if(type == block_text) return ref;
// Otherwise, skipping the first text prevents returning empty blocks.
while(ref.end < limit && *ref.end != '>') ref.end++;
// Increasing ref.end to comply with program conventions on references.
if(ref.end != limit) ref.end++;
// Returning the computed reference.
return ref;
}
/**
* html_parse()
*
* Parses the given html code, defined by a beginning and an end pointer.
* The end pointer must point to the first byte outside the code. In other
* words, it is supposed to be equal to `data + data_length`.
* The generated DOM is stored in an element tree whose entry point is
* global. All data is copied so the loaded code can be freed afterwards.
*
* @param data Pointer on code data.
* @param limit Pointer on first byte outside the code data.
*/
/* TODO: Consider returning a new DOM everytime instead of having a global
* one. */
void html_parse(const char *data, const char *limit)
{
// Parsing pointer.
const char *ptr = data;
// Element reference.
reference_t ref;
// Block type.
enum block_t type = block_tag;
// Looping on elements until limit is reached.
while(ptr < limit)
{
// Getting a reference on the next element.
ref = html_next(ptr, limit, type);
// Updating main pointer.
ptr = ref.end;
// Removing leading and ending spaces for tags.
if(type == block_tag) ref = reftrim(ref);
// Generating an event.
type = html_event(&ref, type);
ptr = ref.end;
}
}
/**
* html_args()
*
* Argument string interpreter. Meant to be used in several calls. A first
* call as html_args(&ref,NULL) initializes the operation, and every
* subsequent calls with two non-null pointers extracts an argument.
*
* @param name Reference pointer. Arguments at initialization, address
* of argument name when extracting.
* @param value Reference pointer. NULL at initialization, address of
* argument value when extracting.
*/
void html_args(reference_t *name, reference_t *value)
{
// Static reference for multiple calls.
static reference_t args;
// Parsing pointer.
const char *ptr = args.begin;
// Initialization.
if(name && !value)
{
// Actually, name is meant to be the full argument string.
args = *name;
// Immediatly returning.
return;
}
// Setting name. This code automatically handles empty names, etc.
while(!isvalid(*ptr) && ptr < args.end) ptr++;
name->begin = ptr;
while(isvalid(*ptr) && ptr < args.end) ptr++;
name->end = ptr;
// Skipping spaces before '=' symbol, if existing.
while(isspace(*ptr) && ptr < args.end) ptr++;
if(ptr == args.end || *ptr != '=')
{
value->begin = value->end = ptr;
return;
}
// Skipping '='.
ptr++;
// Skipping spaces after '='.
while(isspace(*ptr) && ptr < args.end) ptr++;
if(ptr == args.end)
{
value->begin = value->end = ptr;
return;
}
// Getting the value.
if(*ptr == '"')
{
value->begin = ++ptr;
while(*ptr != '"' && ptr < args.end) ptr++;
value->end = ptr;
}
else
{
while(!isvalid(*ptr) && ptr < args.end) ptr++;
value->begin = ptr;
while(isvalid(*ptr) && ptr < args.end) ptr++;
value->end = ptr;
}
args.begin = ptr;
}
/**
* html_clear()
*
* Clears the html module, that is, re-initializing all static pointers
* and previously kept data to ensure interpretation safety.
*/
void html_clear(void)
{
// Using a stack element structure, and a temporary one.
struct el_t *el = top, *tmp;
// Clearing the stack.
while(el)
{
tmp = el->down;
free(el);
el = tmp;
}
// Setting the top pointer.
top = NULL;
}

View File

@ -15,6 +15,8 @@
#include <usb.h>
#include <log.h>
static const char *prev_url = NULL;
struct http_transfer {
char *url;
char *path;
@ -129,6 +131,15 @@ void http_transfer_set_file(http_transfer_t *trans, char *file)
trans->request = utils_get_file(file, &trans->req_length);
}
hashmap_t * http_get_reply_json(http_transfer_t *trans)
{
if (!trans || !trans->completed)
{
return NULL;
}
return utils_json_parse(trans->reply, trans->rep_length);
}
static void *buffer = NULL;
static size_t size;
@ -215,10 +226,19 @@ void http_transfer_send(http_transfer_t *t)
while (true)
{
int code;
usb_fxlink_fill_header(&head, "matrix", "url", strlen(t->url));
usb_write_sync(out, &head, sizeof(usb_fxlink_header_t), false);
usb_write_sync(out, t->url, strlen(t->url), false);
usb_commit_sync(out);
if (!prev_url || strcmp(t->url, prev_url))
{
usb_fxlink_fill_header(&head, "matrix", "url", strlen(t->url));
usb_write_sync(out, &head, sizeof(usb_fxlink_header_t), false);
usb_write_sync(out, t->url, strlen(t->url), false);
usb_commit_sync(out);
if (prev_url)
{
free(prev_url);
}
prev_url = utils_strcpy(t->url);
}
usb_fxlink_fill_header(&head, "matrix", "http", len);
usb_write_sync(out, &head, sizeof(usb_fxlink_header_t), false);
@ -329,12 +349,18 @@ void http_transfer_send(http_transfer_t *t)
int http_transfer_code(http_transfer_t *trans)
{
if (!trans || !trans->completed) return 0;
if (!trans || !trans->completed)
{
return 0;
}
return trans->code;
}
char * http_get_reply_header(http_transfer_t *trans, char *key)
{
if (!trans) return NULL;
if (!trans)
{
return NULL;
}
return utils_hashmap_get(trans->rep_header, key);
}

66
src/img.c Normal file
View File

@ -0,0 +1,66 @@
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#include <libimg.h>
#include <utils.h>
#include <log.h>
img_t utils_downscale(img_t img, int scale)
{
int dw = img.width / scale;
int dh = img.height / scale;
int x, y;
img_t ret = img_create(dw, dh);
for (y = 0; y < dh; y++)
{
for (x = 0; x < dw; x++)
{
int ox, oy;
ox = x * scale;
oy = y * scale;
ret.pixels[y * ret.stride + x] = img.pixels[oy * img.stride + ox];
}
}
return ret;
}
img_t load_png(char *buf, size_t len)
{
img_t img;
int w, h, ch, x, y;
const stbi_uc *stbi = stbi_load_from_memory((const stbi_uc *) buf, len, &w, &h, &ch, 4);
if (!stbi)
{
return img_create(65536, 65536);
}
if (ch != 3 && ch != 4)
{
return img_create(65536, 65536);
}
img = img_create(w, h);
img_clear(img);
for (y = 0; y < h; y++)
{
for (x = 0; x < w; x++)
{
const unsigned char *rgbi = &stbi[(y * w + x) * 4];
uint8_t r = rgbi[0];
uint8_t g = rgbi[1];
uint8_t b = rgbi[2];
uint8_t a = ch == 4 ? rgbi[3] : 255;
img.pixels[img.stride * y + x] = 0x0001;
if (a >= 0x80)
{
img.pixels[img.stride * y + x] = C_RGB(r >> 3, g >> 3, b >> 3);
}
}
}
stbi_image_free((void *) stbi);
return img;
}

View File

@ -1,251 +0,0 @@
#ifndef _DOM_H
#define _DOM_H
/*
Header inclusions.
*/
#include <stdref.h>
/*
Type declarations.
*/
// Just lightening following declarations.
#ifndef _UINT
#define _UINT
typedef unsigned uint;
#endif // _UINT
/*
Composed type definitions.
*/
/**
* DomStyle
*
* This structure handles everything that has to do with styling.
* Comments represent the possible values for the property, in order of
* values. When there is no comment, then the corresponding property is
* associated a numeric value or pointer.
*
* TODO
* - Define DomStyle as an union so that simple tags do not carry
* useless style information.
* - Use a 12-bit bit field for border parameters handling.
*/
struct DomStyle
{
/* TODO: Allow arbitrary colors(this is an fx-**CG**50, damnit!) */
uint background :1; // white, black
uint color :1; // white, black
uint overflowX :2; // visible, hidden, scroll, auto
uint overflowY :2; // visible, hidden, scroll, auto
uint resize :2; // none, horizontal, vertical, both
uint display :2; // none, inline, block, inline-block
uint position :2; // static, absolute, fixed, relative
uint cssFloat :2; // none, left, right
uint verticalAlign :2; // top, middle, bottom
uint top :12;
uint right :12;
uint bottom :12;
uint left :12;
uint width :12;
uint minWidth :12;
uint maxWidth :12;
uint height :12;
uint minHeight :12;
uint maxHeight :12;
uint paddingTop :8;
uint paddingRight :8;
uint paddingBottom :8;
uint paddingLeft :8;
uint borderTop :8;
uint borderRight :8;
uint borderBottom :8;
uint borderLeft :8;
uint borderRadius :8;
uint marginTop :8;
uint marginRight :8;
uint marginBottom :8;
uint marginLeft :8;
uint borderSpacingX :8;
uint borderSpacingY :8;
uint fontSize :8;
int letterSpacing :8;
uint lineHeight :8;
int textIndent :8;
uint cursor :3; // crosshair, default, help, move,
// none, pointer, progress, text
uint visibility :1; // hidden, visible
uint listStyle :4; // 1 01 a A i I x o . - --
uint textAlign :2; // left, center, right, justify
uint textDecoration :2; // none, underline, overline,
// line-trough
uint whiteSpace :2; // normal (wrap), nowrap, pre, pre-wrap
uint borderCollapse :1; // separate, collapse
uint emptyCells :1; // show, hide
uint tabSize :4;
uint wordSpacing :4;
/*
Other elements.
char *backgroundImage;
*/
};
/**
* DomAttribute
*
* This simple structure stores the content of a tag attribute, by name
* and value.
*/
struct DomAttribute
{
char *name;
char *value;
};
/**
* DomElement
*
* This structure represents a node in the DOM element tree. This is the
* mainly manipulated structure in the interpreter.
*/
struct DomElement
{
/*
Basic element properties.
*/
// NUL-terminated tag name (dynamically allocated).
char *tagName;
// Style structure pointer (dynamically allocated).
struct DomStyle *style;
// Node data.
union
{
// Text content, dynamically allocated.
char *textNode;
// Loaded Tex expression (the code is not saved).
/*struct Tex_Expression *tex;*/
};
/*
Children and attributes.
*/
// Children and attribute pointer arrays.
struct DomElement **children;
struct DomAttribute **attributes;
// Children and attribute numbers counters.
uint attrnum :8;
uint childnum :8;
/*
Relations.
*/
// Parent node.
struct DomElement *parent;
// Previous sibling.
struct DomElement *previous;
/*
Possible useful data for later.
unsigned short accessKey;
uint scrollWidth :12;
uint scrollHeight :12;
uint scrollTop :12;
uint scrollLeft :12;
*/
};
typedef struct
{
struct DomElement *html;
uint width :8;
uint height :8;
const char *directory;
const char *stylesheets[10];
int stylesheet :8;
} Window;
/*
Some useful information... (WH = Width/Height, TL = Top/Left)
clientWH = inner + padding - scrollbars.
offsetWH = inner + padding + border.
offsetTL = coords + margin.
scrollWH = overflow + padding - scrollbars.
scrollTL = scrollbars offset.
*/
/*
Function prototypes.
*/
// DomStyle manipulation functions.
struct DomStyle *DomStyle_new(void);
// DomAttribute manipulation functions.
struct DomAttribute *DomAttribute_new(reference_t name, reference_t value);
void DomAttribute_free(struct DomAttribute *attribute);
// DomElement manipulation functions.
struct DomElement *DomElement_new(reference_t name, reference_t args,
struct DomElement *parent, struct DomElement *previous);
void DomElement_free(struct DomElement *element);
struct DomElement *DomElement_addChild(struct DomElement *element,
struct DomElement *child);
struct DomElement *DomElement_getChild(struct DomElement *element, int index);
void DomElement_setTextNode(struct DomElement *element, reference_t text);
const char *DomElement_getAttribute(struct DomElement *element,
const char *attribute_name);
/*void DomElement_resolve(struct DomElement *element);*/
int DomElement_get(struct DomElement *element, int value);
// Non-recursive tree parser.
struct DomElement *Dom_parse(int initialize);
#define DOMElement_totalWidth(e) DomElement_get(e,1)
#define DOMElement_totalHeight(e) DomElement_get(e,2)
#define DOMElement_tokenWidth(e) DomElement_get(e,3)
#define DOMElement_tokenHeight(e) DomElement_get(e,4)
#endif // _DOM_H

View File

@ -1,57 +0,0 @@
#ifndef _WEBCALC_H
#define _WEBCALC_H
// Some headers.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
/*#include "fxlib.h"*/
// Limiting constants definition.
#define CHILD_NUM 20
#define ATTRS_NUM 5
// Project headers.
/*#include "memory.h"
#include "libfont-3.1.h"
#include "tex-0.9.h"*/
#include "DOM.h"
/*#include <display.h>*/
#include <stdref.h>
// Element structure definition.
struct el_t
{
// Opening tag name reference (closing tag carries no info).
reference_t name;
// Tag arguments.
reference_t args;
// Text content, if needed.
reference_t text;
// Associated element.
struct DomElement *dom;
// Linked stack pointer.
struct el_t *down;
};
char *itoa(int n, char *str, int base);
// HTML module functions.
void html_parse(const char *code, const char *end);
void html_args(reference_t *name_or_string, reference_t *value);
struct el_t *html_elem(void);
void html_clear(void);
// CSS module functions.
void css_parse_all(void);
void css_parse(const char *stylesheet_data);
void css_target(reference_t selector, reference_t property, reference_t value);
int css_matches(struct DomElement *element, reference_t target);
void css_apply(struct DomElement *element, reference_t property, reference_t value);
int css_index(reference_t value, int strings, ...);
#endif // _WEBCALC_H

23
src/include/dom.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef MATRIX_DOM_H
#define MATRIX_DOM_H
#include "utils.h"
typedef struct html_elem {
char *name;
hashmap_t *attrs;
struct html_elem *parent; /* NULL if non-existent */
bool has_text;
union {
/* Determined by has_text */
array_t *elems;
char *text;
} children;
} html_elem_t;
extern html_elem_t *dom_parse_element(char *);
extern void dom_free_element(html_elem_t *);
#endif

View File

@ -3,5 +3,9 @@
extern void easter_goda(void);
extern void easter_dvd_frame(const char *);
extern void easter_strongest(void);
/* This one's a fool's errand. */
extern void easter_credit(void);
#endif

View File

@ -27,6 +27,7 @@ extern void http_transfer_send(http_transfer_t *);
extern int http_transfer_code(http_transfer_t *);
extern char * http_get_reply_header(http_transfer_t *, char *);
extern void * http_get_reply_data(http_transfer_t *, size_t *);
extern hashmap_t * http_get_reply_json(http_transfer_t *);
extern void http_transfer_free(http_transfer_t *);

View File

@ -10,6 +10,7 @@
#include <utils.h>
#include <http.h>
typedef enum m_delegation {
/* Terms used in 3. Server Discovery */
DELEG_PROMPT,
@ -32,6 +33,7 @@ typedef struct m_event {
char *state_key;
bool redacted;
/* TODO: Unsigned data */
} m_event_t;
typedef struct m_room {
@ -40,6 +42,7 @@ typedef struct m_room {
size_t joined;
array_t *timeline;
hashmap_t *relations;
hashmap_t *nicks;
} m_room_t;
@ -53,7 +56,21 @@ typedef struct m_user {
char *sync_next;
hashmap_t *rooms;
hashmap_t *images;
hashmap_t *mxcs;
} m_user_t;
typedef struct m_room_chunk {
char *avatar_url;
char *canonical_alias;
bool guest_can_join;
char *join_rule;
char *name;
int num_joined_members;
char *room_id;
char *room_type;
char *topic;
bool world_readable;
} m_room_chunk_t;
/* Tries to find a delegation point with well-known */
extern m_delegation_t matrix_delegate(char *, char **);
@ -64,6 +81,12 @@ extern bool matrix_supports_password(char *);
/* Tries to login to a specific server with a password*/
extern m_user_t *matrix_login(char *, char *, char *);
/* Saves a user into Flash memory */
extern void matrix_save(m_user_t *);
extern m_user_t * matrix_load(void);
extern void matrix_logout(m_user_t *);
/* Attempts a *basic* initial sync request to get the barebone
* information. */
extern void matrix_initial_sync(m_user_t *);
@ -72,17 +95,24 @@ extern void matrix_initial_sync(m_user_t *);
* mind you!) */
extern m_event_t *matrix_parse_event(hashmap_t *, bool);
/* Retrieves an event from the server given its ID */
extern hashmap_t *matrix_event_to_json(m_user_t *, m_event_t *);
/* Frees data associated with the event. */
extern void matrix_free_event(m_event_t *);
/* Fetch a (state_type,state_key) pair for the room */
extern hashmap_t *matrix_state_fetch(m_user_t *, m_room_t *, char *, char *);
/* Sets a state element in the room if possible. */
extern bool matrix_state_set(m_user_t *, m_room_t *, char *, char *, hashmap_t *);
/* Finds the room's actual name from its state.
*
* Please mind that it is stored on the heap, and as such, needs to be freed.
*/
extern char *matrix_resolve_name(m_user_t *, m_room_t *);
extern char *matrix_resolve_avatar(m_user_t *, m_room_t *);
extern char *matrix_resolve_topic(m_user_t *, m_room_t *);
@ -90,6 +120,16 @@ extern char *matrix_resolve_topic(m_user_t *, m_room_t *);
*/
extern char *matrix_resolve_nick(m_user_t *, char *, m_room_t *);
/* Sets a user's global displayname */
extern void matrix_set_displayname(m_user_t *, char *);
/* Gets a user's global displayname */
extern char * matrix_get_displayname(m_user_t *);
extern char * matrix_get_avatar(m_user_t *);
/* Gets a user's local avatar */
extern char * matrix_get_lavatar(m_user_t *, char *);
/* Attaches a token to a HTTP transfer*/
extern void matrix_set_token(http_transfer_t *, m_user_t *);
@ -101,6 +141,8 @@ extern uint16_t matrix_hash(char *);
*
* Returns true if anything has changed. */
extern bool matrix_update_room_history(m_user_t *, m_room_t *, m_event_t *e, int);
/* Uses /messages to start populating a timeline from scratch */
extern void matrix_populate_messages(m_user_t *, m_room_t *, int);
extern ssize_t is_in_timeline(m_room_t *, char *);
@ -108,8 +150,32 @@ extern ssize_t is_in_timeline(m_room_t *, char *);
extern void matrix_send_event(m_user_t *, m_room_t *, char *, hashmap_t *);
extern void matrix_show_event(m_room_t *, m_user_t *, m_event_t *, jwidget *);
extern void matrix_redact(m_user_t *, m_event_t *, char *);
extern char *matrix_send_file(m_user_t *, char *, char *);
extern char *matrix_get_file(m_user_t *, char *, size_t *);
/* Get public rooms in a server's directory(defaults to the user's) */
extern array_t *matrix_get_directory(m_user_t *, char *, int);
extern void matrix_free_directory(array_t *);
extern bool matrix_set_nick(m_user_t *, m_room_t *, char *);
extern char *matrix_get_mxc_avatar(m_user_t *, char *, size_t *, int);
extern void *matrix_image_from_mxc(void *, m_user_t *, char *, int);
extern void *matrix_image_from_user(void *, m_user_t *, char *);
extern void *matrix_image_from_room_user(void *,m_user_t *,m_room_t *,char *);
/* Fetches the public room list for a server(and stores/loads a batch) */
extern array_t *matrix_fetch_roomdir(m_user_t *, int, char *, char **);
/* Joins a room with a given room ID */
extern m_room_t * matrix_join_room(m_user_t *, char *);
extern char *matrix_get_relation_source(m_event_t *);
extern char *matrix_get_relation_type(m_event_t *);
extern json_value_t *matrix_get_relation_property(m_event_t *, char *);
extern void matrix_add_relation(char *, m_room_t *, m_event_t *);
#endif

7985
src/include/stb_image.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,45 +0,0 @@
#ifndef _STDREF_H
#define _STDREF_H 1
/*
Header inclusions.
*/
/* We actually *do* have those! */
#include <string.h>
#include <stdlib.h>
/*#include "fxlib.h"*/
/*
Composed types definitions.
*/
// reference_t structure definition.
typedef struct
{
// Beginning of referenced area. Included.
const char *begin;
// End of referenced area. Excluded.
const char *end;
} reference_t;
/*
Function prototypes.
*/
// Tests if a reference is invalid or null.
int refnull(reference_t ref);
// Extracts the referenced text in an allocated pointer.
char *refxtrct(reference_t ref);
// Unreferences the spaces at the beginning and end of the referenced area.
reference_t reftrim(reference_t ref);
// Compares a reference and a string.
int refscmp(reference_t ref, const char *str);
// Compares a reference and a string, in the limit of `length` characters.
int refsncmp(reference_t ref, const char *str, int length);
#endif // _STDREF_H

View File

@ -2,6 +2,7 @@
#define MATRIX_UI_H
#include <utils.h>
#include <matrix.h>
#include <sys/types.h>
@ -10,9 +11,7 @@
#include <justui/jfkeys.h>
#include <justui/jframe.h>
#include <gint/display.h>
#include <matrix.h>
#include <libimg.h>
/* A value, with a function to free said value */
@ -40,6 +39,8 @@ typedef struct ui_session {
/* Public data for everyone! */
m_user_t *user;
m_room_t *room;
bool rooms_dirty;
char *new_id;
} ui_session_t;
struct ui_screen;
@ -102,6 +103,7 @@ typedef struct jmlist {
jrect rect;
} jmlist;
extern uint16_t JMLIST_ITEM_OVERFLEW;
extern uint16_t JMLIST_ITEM_CHOSEN;
extern uint16_t JMLIST_ITEM_CHANGE;
extern uint16_t JMLIST_ITEM_ADDED;
@ -114,8 +116,15 @@ extern int jmlist_prepend_item(jmlist *, void *);
extern int jmlist_select(jmlist *, int);
extern bool jmlist_scroll(jmlist *, jrect);
extern jrect jmlist_scrolled(jmlist *, jrect);
extern void jmlist_redo(jmlist *, int);
typedef struct jmimage {
jwidget widget;
img_t img;
} jmimage;
extern jmimage *jmimage_create(void *, img_t);
extern jpainted *ui_image(void *, const bopti_image_t *);
extern jpainted *ui_blurhash(void *, char *);
#endif

View File

@ -16,7 +16,14 @@ extern void * ui_rooms_init(ui_screen_t *that);
extern void * ui_rooms_focus(ui_screen_t *that);
extern void * ui_rooms_event(ui_screen_t *that, jevent e);
extern void * ui_room_init(ui_screen_t *that);
extern void * ui_room_event(ui_screen_t *that, jevent e);
extern void * ui_roomdir_init(ui_screen_t *that);
extern void * ui_roomdir_event(ui_screen_t *that, jevent e);
extern void * ui_accinfo_init(ui_screen_t *that);
extern void * ui_accinfo_focus(ui_screen_t *that);
extern void * ui_accinfo_event(ui_screen_t *that, jevent e);
#endif

View File

@ -1,18 +1,39 @@
#ifndef MATRIX_UTILS_H
#define MATRIX_UTILS_H
#include <libimg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#define B * 1
#define KB * (1024 B)
#define MB * (1024 KB)
/* Copies a string into a new heap-allocated string */
extern char * utils_strcpy(char *);
extern char * utils_strcat(char *, char *);
extern char * utils_itoa(int i);
extern char * utils_basename(char *);
extern int utils_htov(char c);
extern char * utils_unitoutf(uint16_t);
extern char * utils_sprintf(char *, ...);
/* Creates a fd/FILE * that maps to memory */
extern int utils_openmem(void *, size_t);
extern FILE *utils_fopenmem(void *, size_t);
extern char * utils_get_file(char *, size_t *);
extern void utils_write_file(char *, char *, size_t);
extern void utils_delete_file(char *);
extern uint32_t utils_total_mem(void);
extern uint32_t utils_used_mem(void);
extern img_t load_png(char *buf, size_t len);
extern img_t utils_downscale(img_t, int);
typedef struct hashmap hashmap_t;
@ -30,6 +51,7 @@ typedef struct array array_t;
extern array_t * utils_new_array(void);
extern size_t utils_array_add(array_t *, void *);
extern void utils_array_shift(array_t *, size_t);
extern void utils_array_invert(array_t *);
extern size_t utils_array_size(array_t *);
extern void * utils_array_get(array_t *, size_t);
extern void utils_array_set(array_t *, size_t, void *);
@ -47,7 +69,7 @@ typedef enum json_value_type {
} json_value_type_t;
extern hashmap_t * utils_json_parse(char *, size_t);
extern char * utils_json_write(hashmap_t *, size_t *);
extern char * utils_json_write (hashmap_t *, size_t *);
extern json_value_type_t utils_json_value_type(json_value_t *);
extern void utils_free_json_value(json_value_t *);
extern void utils_free_json(hashmap_t *);
@ -66,6 +88,4 @@ extern json_value_t * utils_boolean_as_json(bool);
extern json_value_t * utils_null_as_json(void);
extern hashmap_t *utils_json_copy(hashmap_t *);
extern uint16_t *utils_decode_blurhash(char *, int *, int *);
#endif

162
src/include/yxml.h Normal file
View File

@ -0,0 +1,162 @@
/* Copyright (c) 2013-2014 Yoran Heling
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef YXML_H
#define YXML_H
#include <stdint.h>
#include <stddef.h>
#if defined(_MSC_VER) && !defined(__cplusplus) && !defined(inline)
#define inline __inline
#endif
/* Full API documentation for this library can be found in the "yxml.md" file
* in the yxml git repository, or online at http://dev.yorhel.nl/yxml/man */
typedef enum {
YXML_EEOF = -5, /* Unexpected EOF */
YXML_EREF = -4, /* Invalid character or entity reference (&whatever;) */
YXML_ECLOSE = -3, /* Close tag does not match open tag (<Tag> .. </OtherTag>) */
YXML_ESTACK = -2, /* Stack overflow (too deeply nested tags or too long element/attribute name) */
YXML_ESYN = -1, /* Syntax error (unexpected byte) */
YXML_OK = 0, /* Character consumed, no new token present */
YXML_ELEMSTART = 1, /* Start of an element: '<Tag ..' */
YXML_CONTENT = 2, /* Element content */
YXML_ELEMEND = 3, /* End of an element: '.. />' or '</Tag>' */
YXML_ATTRSTART = 4, /* Attribute: 'Name=..' */
YXML_ATTRVAL = 5, /* Attribute value */
YXML_ATTREND = 6, /* End of attribute '.."' */
YXML_PISTART = 7, /* Start of a processing instruction */
YXML_PICONTENT = 8, /* Content of a PI */
YXML_PIEND = 9 /* End of a processing instruction */
} yxml_ret_t;
/* When, exactly, are tokens returned?
*
* <TagName
* '>' ELEMSTART
* '/' ELEMSTART, '>' ELEMEND
* ' ' ELEMSTART
* '>'
* '/', '>' ELEMEND
* Attr
* '=' ATTRSTART
* "X ATTRVAL
* 'Y' ATTRVAL
* 'Z' ATTRVAL
* '"' ATTREND
* '>'
* '/', '>' ELEMEND
*
* </TagName
* '>' ELEMEND
*/
typedef struct {
/* PUBLIC (read-only) */
/* Name of the current element, zero-length if not in any element. Changed
* after YXML_ELEMSTART. The pointer will remain valid up to and including
* the next non-YXML_ATTR* token, the pointed-to buffer will remain valid
* up to and including the YXML_ELEMEND for the corresponding element. */
char *elem;
/* The last read character(s) of an attribute value (YXML_ATTRVAL), element
* data (YXML_CONTENT), or processing instruction (YXML_PICONTENT). Changed
* after one of the respective YXML_ values is returned, and only valid
* until the next yxml_parse() call. Usually, this string only consists of
* a single byte, but multiple bytes are returned in the following cases:
* - "<?SomePI ?x ?>": The two characters "?x"
* - "<![CDATA[ ]x ]]>": The two characters "]x"
* - "<![CDATA[ ]]x ]]>": The three characters "]]x"
* - "&#N;" and "&#xN;", where dec(n) > 127. The referenced Unicode
* character is then encoded in multiple UTF-8 bytes.
*/
char data[8];
/* Name of the current attribute. Changed after YXML_ATTRSTART, valid up to
* and including the next YXML_ATTREND. */
char *attr;
/* Name/target of the current processing instruction, zero-length if not in
* a PI. Changed after YXML_PISTART, valid up to (but excluding)
* the next YXML_PIEND. */
char *pi;
/* Line number, byte offset within that line, and total bytes read. These
* values refer to the position _after_ the last byte given to
* yxml_parse(). These are useful for debugging and error reporting. */
uint64_t byte;
uint64_t total;
uint32_t line;
/* PRIVATE */
int state;
unsigned char *stack; /* Stack of element names + attribute/PI name, separated by \0. Also starts with a \0. */
size_t stacksize, stacklen;
unsigned reflen;
unsigned quote;
int nextstate; /* Used for '@' state remembering and for the "string" consuming state */
unsigned ignore;
unsigned char *string;
} yxml_t;
#ifdef __cplusplus
extern "C" {
#endif
void yxml_init(yxml_t *, void *, size_t);
yxml_ret_t yxml_parse(yxml_t *, int);
/* May be called after the last character has been given to yxml_parse().
* Returns YXML_OK if the XML document is valid, YXML_EEOF otherwise. Using
* this function isn't really necessary, but can be used to detect documents
* that don't end correctly. In particular, an error is returned when the XML
* document did not contain a (complete) root element, or when the document
* ended while in a comment or processing instruction. */
yxml_ret_t yxml_eof(yxml_t *);
#ifdef __cplusplus
}
#endif
/* Returns the length of the element name (x->elem), attribute name (x->attr),
* or PI name (x->pi). This function should ONLY be used directly after the
* YXML_ELEMSTART, YXML_ATTRSTART or YXML_PISTART (respectively) tokens have
* been returned by yxml_parse(), calling this at any other time may not give
* the correct results. This function should also NOT be used on strings other
* than x->elem, x->attr or x->pi. */
static inline size_t yxml_symlen(yxml_t *x, const char *s) {
return (x->stack + x->stacklen) - (const unsigned char*)s;
}
#endif
/* vim: set noet sw=4 ts=4: */

73
src/jmimage.c Normal file
View File

@ -0,0 +1,73 @@
#include <ui.h>
#include <stdlib.h>
#include <log.h>
static int jmimage_type_id = -1;
jmimage *jmimage_create(void *parent, img_t img)
{
jmimage *image;
if (jmimage_type_id < 0)
{
return NULL;
}
image = malloc(sizeof(jmimage));
jwidget_init(&image->widget, jmimage_type_id, parent);
jwidget_set_fixed_size(image, img.width, img.height);
jwidget_set_clipped(image, true);
image->img = img;
return image;
}
static void jmimage_poly_render(void *i, int x, int y)
{
jmimage *image = i;
int px, py;
for (py = 0; py < image->img.height; py++)
{
for (px = 0; px < image->img.width; px++)
{
int X = px + x;
int Y = py + y;
unsigned int pxl = image->img.pixels[image->img.stride * py + px];
if (pxl != 0x0001)
{
dpixel(X, Y, pxl);
}
}
}
}
static void jmimage_poly_csize(void *i)
{
jmimage *image = i;
image->widget.w = image->img.width;
image->widget.h = image->img.height;
}
static void jmimage_poly_destroy(void *i)
{
jmimage *image = i;
/*img_destroy(image->img);*/
(void) image; /* TODO: Delete if flag is set */
}
static jwidget_poly type_jmimage = {
.name = "jmlist",
.csize = jmimage_poly_csize,
.render = jmimage_poly_render,
.event = NULL,
.destroy = jmimage_poly_destroy,
};
__attribute__((constructor(2001)))
static void j_register_jmimage(void)
{
jmimage_type_id = j_register_widget(&type_jmimage, "jwidget");
}

View File

@ -6,10 +6,10 @@
#include <justui/jwidget-api.h>
#include <utils.h>
#include <log.h>
static int jmlist_type_id = -1;
uint16_t JMLIST_ITEM_OVERFLEW;
uint16_t JMLIST_ITEM_CHOSEN;
uint16_t JMLIST_ITEM_CHANGE;
uint16_t JMLIST_ITEM_ADDED;
@ -33,15 +33,14 @@ jmlist *jmlist_create(void *parent, jmlist_set_widget set, jmlist_free_data f)
list->box = jwidget_create(list->frame);
jlayout_set_vbox(list->box)->spacing = 1;
jwidget_set_maximum_width(list->box, DWIDTH);
jwidget_set_fixed_width(list->box, DWIDTH);
jframe_set_align(list->frame, J_ALIGN_LEFT, J_ALIGN_TOP);
list->arr = utils_new_array();
list->selected = -1;
list->set = set;
list->end = f;
jframe_set_align(list->frame, J_ALIGN_LEFT, J_ALIGN_TOP);
jwidget_set_stretch(list->frame, 1, 1, false);
jwidget_set_stretch(list->box, 1, 1, false);
@ -93,7 +92,6 @@ int jmlist_prepend_item(jmlist *list, void *data)
utils_array_shift(list->arr, 1);
utils_array_set(list->arr, 0, item);
list->set(item, 0);
list->offset_x = -(jwidget_full_width(list->box));
jwidget_emit(list, (jevent){ .type = JMLIST_ITEM_ADDED });
@ -102,7 +100,6 @@ int jmlist_prepend_item(jmlist *list, void *data)
int jmlist_select(jmlist *list, int idx)
{
jmlist_item *item;
size_t oidx;
if (!list)
{
@ -114,11 +111,9 @@ int jmlist_select(jmlist *list, int idx)
if (item && item->widget)
{
jwidget *w = item->widget;
log_text("%s: unselecting", __func__);
jwidget_set_background(w, C_NONE);
log_text("%s: unselected", __func__);
}
if (idx >= utils_array_size(list->arr))
if ((size_t) idx >= utils_array_size(list->arr))
{
idx = utils_array_size(list->arr) - 1;
}
@ -127,47 +122,43 @@ int jmlist_select(jmlist *list, int idx)
idx = 0;
}
log_text("%s: getting", __func__);
item = utils_array_get(list->arr, idx);
if (!item)
{
log_text("%s: got %d items", __func__, utils_array_size(list->arr));
return list->selected;
}
log_text("%s: got", __func__);
list->selected = idx;
log_text("%s: setting bg %p %d", __func__, item, idx);
jwidget_set_background(item->widget, C_RGB(1, 19, 27));
log_text("%s: set bg %p", __func__, item);
jmlist_scroll(
list,
(jrect){
.x = list->offset_x,
.x = list->frame->scroll_x,
.y = (item->widget->y + list->box->y) - list->frame->widget.y,
.w = jwidget_content_width(item->widget),
.h = jwidget_full_height(item->widget)
}
);
log_text("%s: scrolled", __func__);
jwidget_emit(list, (jevent){ .type = JMLIST_ITEM_CHANGE });
log_text("%s: emitted", __func__);
return idx;
}
extern bool jmlist_scroll(jmlist *list, jrect rect)
{
int16_t oldscroll;
if (!list)
{
return false;
}
oldscroll = list->frame->scroll_x;
jframe_scroll_to_region(
list->frame,
rect,
false
);
list->frame->scroll_x = oldscroll;
list->rect = rect;
list->widget.dirty = 1;
return true;
@ -177,10 +168,6 @@ extern bool jmlist_scroll(jmlist *list, jrect rect)
static bool jmlist_poly_event(void *ml0, jevent e)
{
jmlist *list = ml0;
jevent ret;
int key;
if (e.type != JWIDGET_KEY || list->selected < 0)
@ -193,19 +180,30 @@ static bool jmlist_poly_event(void *ml0, jevent e)
{
case KEY_UP:
if (e.key.type == KEYEV_HOLD) return false;
if (jmlist_selected(list) <= 0)
{
jwidget_emit(list, (jevent){ .type = JMLIST_ITEM_OVERFLEW });
return true;
}
jmlist_select(list, jmlist_selected(list) - 1);
return true;
case KEY_DOWN:
if (e.key.type == KEYEV_HOLD) return false;
if (jmlist_selected(list) + 1 >= (int) utils_array_size(list->arr))
{
jwidget_emit(list, (jevent){ .type = JMLIST_ITEM_OVERFLEW });
return true;
}
jmlist_select(list, jmlist_selected(list) + 1);
return true;
case KEY_LEFT:
list->offset_x -= DWIDTH / 10;
list->frame->scroll_x -= DWIDTH / 10;
if (list->frame->scroll_x < 0) list->frame->scroll_x = 0;
list->widget.dirty = 1;
jmlist_select(list, jmlist_selected(list));
return true;
case KEY_RIGHT:
list->offset_x += DWIDTH / 10;
list->frame->scroll_x += DWIDTH / 10;
list->widget.dirty = 1;
jmlist_select(list, jmlist_selected(list));
return true;
@ -217,6 +215,28 @@ static bool jmlist_poly_event(void *ml0, jevent e)
return false;
}
void jmlist_redo(jmlist *list, int selected)
{
jmlist_item *item;
if (!list)
{
return;
}
item = utils_array_get(list->arr, selected);
if (item->widget)
{
/* Destroy it's children */
size_t i;
size_t children = item->widget->child_count;
for (i = 0; i < children; i++)
{
jwidget_destroy(item->widget->children[0]);
}
}
/* ... then put everything back in Ordung */
list->set(item, selected);
}
static void jmlist_poly_destroy(void *ml0)
{
jmlist *list = ml0;
@ -248,6 +268,7 @@ __attribute__((constructor(2001)))
static void j_register_jmlist(void)
{
jmlist_type_id = j_register_widget(&type_jmlist, "jwidget");
JMLIST_ITEM_OVERFLEW = j_register_event();
JMLIST_ITEM_CHOSEN = j_register_event();
JMLIST_ITEM_CHANGE = j_register_event();
JMLIST_ITEM_ADDED = j_register_event();

View File

@ -7,8 +7,6 @@
#include <ctype.h>
#include <math.h>
#include <log.h>
typedef union val_as {
hashmap_t *object;
array_t *array;
@ -188,16 +186,18 @@ json_token_t *next_token(json_tokeniser_t *tokeniser)
{
json_token_t *token = NULL;
int c, escape;
char *str = NULL, *strcpy = NULL;
char *str = NULL, *strcpy = NULL, *str1;
float number = 0;
size_t off;
size_t off = 0;
int neg = 1;
int deci;
int deci = 0;
ssize_t expidx = 0;
bool negative_exp = false;
bool have_to_ungetch = true;
uint16_t unicode;
if (!tokeniser)
{
return NULL;
@ -224,10 +224,26 @@ loop:
case '"':
case '\\':
case '/':
case 'u': /* TODO: Implement UNICODE */
strcpy = utils_strcat(str, to_string(escape));
if (str) free(str);
str = strcpy;
break;
case 'u': /* TODO: Implement UNICODE(16-bit codepoints) */
unicode = 0x0000;
unicode += utils_htov(tokeniser_getch(tokeniser));
unicode <<= 4;
unicode += utils_htov(tokeniser_getch(tokeniser));
unicode <<= 4;
unicode += utils_htov(tokeniser_getch(tokeniser));
unicode <<= 4;
unicode += utils_htov(tokeniser_getch(tokeniser));
str1 = utils_unitoutf(unicode);
strcpy = utils_strcat(str, str1);
if (str) free(str);
free(str1);
str = strcpy;
break;
case 'b':
strcpy = utils_strcat(str, to_string('\b'));
@ -297,6 +313,7 @@ loop:
goto loop;
}
tokeniser_ungetc(tokeniser);
/* Fallthrough */ /* gcc stop being a dumbass */
case JSON_STATE_NUMBER_MAIN:
c = tokeniser_getch(tokeniser);
if (isdigit(c))
@ -367,7 +384,10 @@ loop:
deci *= 10;
goto loop;
}
deci /= 10;
if (deci != 0)
{
deci /= 10;
}
number *= powf(10, (negative_exp ? -1 : 1) * deci);
number *= neg;
@ -464,7 +484,6 @@ static json_parser_t *parser_create(json_tokeniser_t *tokeniser)
static void parser_free(json_parser_t *parser)
{
size_t i;
json_token_t *token;
array_t *arr;
if (!parser)
@ -861,7 +880,7 @@ void utils_free_json_value(json_value_t *value)
/* == WRITE ==
* Functions to *write* JSON.
*/
static char *json_write_value(char *in, size_t *size, json_value_t *val);
static char *json_write_value(char *i,size_t *s,json_value_t *v);
static char *json_write_null(char *in, size_t *size, json_value_t *val)
{
@ -924,7 +943,7 @@ static char *json_write_boolean(char *in, size_t *size, json_value_t *val)
return incpy;
}
static char *json_write_array(char *in, size_t *size, json_value_t *val)
static char *json_write_array(char *in, size_t *size,json_value_t *val)
{
char *incpy;
size_t i;
@ -973,7 +992,7 @@ static char *json_write_array(char *in, size_t *size, json_value_t *val)
return incpy;
}
static char *json_write_object(char *in, size_t *size, json_value_t *val)
static char *json_write_object(char *in, size_t *size,json_value_t *val)
{
char *incpy;
char *key;
@ -1008,6 +1027,7 @@ static char *json_write_object(char *in, size_t *size, json_value_t *val)
in = incpy;
(*size)++;
}
incpy = utils_strcat(in, "\"");
free(in);
in = incpy;
@ -1038,9 +1058,6 @@ static char *json_write_string(char *in, size_t *size, json_value_t *val)
char *string;
char chr[3];
bool first = true;
json_value_t *value;
if (!size || !val)
{
return NULL;
@ -1157,7 +1174,7 @@ char * json_write_value(char *in, size_t *size, json_value_t *val)
char * utils_json_write(hashmap_t *obj, size_t *size)
{
size_t len = 0;
char *str = NULL, *str_copy;
char *str = NULL;
json_value_t *val = NULL;
@ -1263,8 +1280,6 @@ void utils_free_json(hashmap_t *hm)
}
json_value_t *json_copy_value(json_value_t *val)
{
json_value_t *copied;
array_t *arr_copy;
size_t i;
if (!val)
@ -1293,6 +1308,7 @@ json_value_t *json_copy_value(json_value_t *val)
case JSON_NULL:
return utils_null_as_json();
}
return NULL;
}
hashmap_t *utils_json_copy(hashmap_t *json)

77
src/keymap_translate.h Normal file
View File

@ -0,0 +1,77 @@
/* INTERNAL, DO NOT USE INSIDE HEADER */
#include <gint/keyboard.h>
static int key_id(int keycode)
{
uint col = (keycode & 0x0f) - 1;
uint row = 9 - ((keycode & 0xf0) >> 4);
if(col > 5 || row > 8) return -1;
return 6 * row + col;
}
static uint16_t map_flat[48] = {
0,0x2468, 0, 0, 0, 0,
0, 0xB2 , '^', 0, 0, 0,
0xE9,0xC9, 0, '~', 0, 0,
0xE8,0xC8,'(', ')', ',', '=',
'7', '8', '9', 0, 0, 0,
'4', '5', '6', '*', '/', 0,
'1', '2', '3', '+', '-', 0,
'0', '.', 'e', '-', 0, 0,
};
static uint16_t map_alpha[36] = {
'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 0, 0, 0,
'p', 'q', 'r', 's', 't', 0,
'u', 'v', 'w', 'x', 'y', 0,
'z', ' ', '"', 0, 0, 0,
};
uint32_t uni_keymap_translate(int key, bool shift, bool alpha)
{
int id = key_id(key);
if (key >= KEY_F1 && key <= KEY_F6)
{
return 0x0000;
}
if(id < 0) return 0;
if(!shift && !alpha) {
return map_flat[id - 6];
}
if(shift && !alpha) {
if(key == KEY_MUL) return '{';
if(key == KEY_DIV) return '}';
if(key == KEY_ADD) return '[';
if(key == KEY_SUB) return ']';
if(key == KEY_DOT) return '=';
if(key == KEY_EXP) return 0x3c0; // 'π'
if(key == KEY_0) return '!';
if(key == KEY_6) return '<';
if(key == KEY_3) return '>';
if(key == KEY_5) return '!';
if(key == KEY_2) return '?';
if(key == KEY_4) return ':';
if(key == KEY_1) return ';';
}
if(!shift && alpha) {
/* The first 3 rows have no useful characters */
if (key == KEY_X2) return 0x2468;
if (key == KEY_CARET) return 0x03B8;
return (id < 18) ? 0 : map_alpha[id - 18];
}
if(shift && alpha) {
int c = uni_keymap_translate(key, false, true);
if (key == KEY_X2) return 0x2468;
if (key == KEY_CARET) return 0x0398;
return (c >= 'a' && c <= 'z') ? (c & ~0x20) : c;
}
return 0;
}

View File

@ -9,6 +9,7 @@
#include <utils.h>
#include <http.h>
#include <log.h>
bool matrix_supports_password(char *server)
{
@ -67,6 +68,62 @@ end:
}
return ret;
}
void matrix_save(m_user_t *user)
{
#define save_string(n, v) utils_hashmap_add(serialised, #n, utils_string_as_json(utils_strcpy(v)))
hashmap_t *serialised;
char *data;
size_t len;
if (!user)
{
return;
}
serialised = utils_new_hashmap();
save_string(id, user->id);
save_string(token, user->access_token);
save_string(did, user->device_id);
save_string(server, user->server);
data = utils_json_write(serialised, &len);
utils_write_file("user.json", data, len);
utils_free_json(serialised);
}
m_user_t * matrix_load(void)
{
#define retrieve_string(v, k) v = utils_strcpy(utils_json_as_string(utils_hashmap_get(json, #k)))
m_user_t *user = NULL;
hashmap_t *json;
char *data;
size_t len;
log_text("loading...");
if (!(data = utils_get_file("user.json", &len)))
{
return NULL;
}
json = utils_json_parse(data, len);
user = malloc(sizeof(m_user_t));
retrieve_string(user->id, id);
retrieve_string(user->access_token, token);
retrieve_string(user->device_id, did);
retrieve_string(user->server, server);
user->rooms = utils_new_hashmap();
user->images = utils_new_hashmap();
user->mxcs = utils_new_hashmap();
log_text("server='%s'", user->server);
log_text("id='%s'", user->id);
log_text("device_id='%s'", user->device_id);
utils_free_json(json);
free(data);
return user;
}
m_user_t *matrix_login(char *server, char *user, char *password)
{
m_user_t *ret;
@ -132,7 +189,6 @@ m_user_t *matrix_login(char *server, char *user, char *password)
if (http_transfer_code(trans) != 200)
{
char *msg = http_get_reply_data(trans, &data_len);
ret = NULL;
goto end;
}
@ -155,6 +211,8 @@ m_user_t *matrix_login(char *server, char *user, char *password)
ret->sync_next = NULL;
ret->rooms = utils_new_hashmap();
ret->images = utils_new_hashmap();
ret->mxcs = utils_new_hashmap();
end:
if (trans)
{
@ -162,6 +220,33 @@ end:
}
return ret;
}
void matrix_logout(m_user_t *user)
{
http_transfer_t *trans;
if (!user)
{
return;
}
trans = http_transfer_create(
HTTP_POST,
user->server, "/_matrix/client/v3/logout"
);
matrix_set_token(trans, user);
http_transfer_send(trans);
http_transfer_free(trans);
free(user->access_token);
free(user->device_id);
free(user->id);
free(user->server);
if (user->sync_next)
{
free(user->sync_next);
}
/* TODO: End with user->rooms */
free(user);
utils_delete_file("user.json");
}
void matrix_set_token(http_transfer_t *trans, m_user_t *user)
{
char *bearer;
@ -182,6 +267,113 @@ void matrix_set_token(http_transfer_t *trans, m_user_t *user)
http_transfer_add_header(trans, "Authorization", bearer);
free(bearer);
}
void matrix_set_displayname(m_user_t *user, char *dname)
{
char *path;
size_t len;
http_transfer_t *trans;
hashmap_t *dn;
if (!user || !dname)
{
return;
}
len = snprintf(
NULL, 0,
"/_matrix/client/v3/profile/%s/displayname", user->id
);
path = malloc(len + 1);
snprintf(
path, len + 1,
"/_matrix/client/v3/profile/%s/displayname", user->id
);
trans = http_transfer_create(HTTP_PUT, user->server, path);
free(path);
matrix_set_token(trans, user);
dn = utils_new_hashmap();
utils_hashmap_add(
dn,
"displayname", utils_string_as_json(utils_strcpy(dname))
);
http_transfer_set_json(trans, dn);
http_transfer_send(trans);
http_transfer_free(trans);
}
char * matrix_get_displayname(m_user_t *user)
{
char *displayname;
char *path;
http_transfer_t *trans;
hashmap_t *reply;
if (!user)
{
return NULL;
}
path = utils_sprintf("/_matrix/client/v3/profile/%s", user->id);
trans = http_transfer_create(HTTP_GET, user->server, path);
free(path);
http_transfer_send(trans);
if (http_transfer_code(trans) != 200)
{
http_transfer_free(trans);
return NULL;
}
reply = http_get_reply_json(trans);
displayname = utils_json_as_string(
utils_hashmap_get(reply, "displayname")
);
displayname = utils_strcpy(displayname);
utils_free_json(reply);
http_transfer_free(trans);
return displayname;
}
char * matrix_get_lavatar(m_user_t *user, char *id)
{
char *displayname;
char *path;
http_transfer_t *trans;
hashmap_t *reply;
if (!user)
{
return NULL;
}
path = utils_sprintf("/_matrix/client/v3/profile/%s", id);
trans = http_transfer_create(HTTP_GET, user->server, path);
free(path);
http_transfer_send(trans);
if (http_transfer_code(trans) != 200)
{
http_transfer_free(trans);
return NULL;
}
reply = http_get_reply_json(trans);
displayname = utils_json_as_string(
utils_hashmap_get(reply, "avatar_url")
);
displayname = utils_strcpy(displayname);
utils_free_json(reply);
http_transfer_free(trans);
return displayname;
}
char * matrix_get_avatar(m_user_t *user)
{
if (!user)
{
return NULL;
}
return matrix_get_lavatar(user, user->id);
}
#define COLORS 6
const uint16_t palette[COLORS] = {
@ -194,7 +386,6 @@ const uint16_t palette[COLORS] = {
};
uint16_t matrix_hash(char *id)
{
uint16_t i;
uint32_t hash = 4373;
if (!id)
{

View File

@ -3,6 +3,7 @@
#include <gint/hardware.h>
#include <gint/display.h>
#include <gint/kmalloc.h>
#include <gint/clock.h>
#include <gint/usb.h>
#include <gint/rtc.h>
#include <gint/fs.h>
@ -56,6 +57,8 @@ static void story_of_usb_comm(void)
log_text("=============================");
}
extern font_t uf8x9;
int main(void)
{
ui_session_t *ui;
@ -63,57 +66,68 @@ int main(void)
int key = 0;
int in, out;
usb_fxlink_header_t reply;
uint8_t major, minor;
short int tmp;
uint32_t self_addr = (uintptr_t) main;
uint32_t top_addr = self_addr & 0xFF000000;
dfont(&uf8x9);
srand(rtc_ticks());
/* Can we use extra memory? Check if this is an fx-CG50 and
* that main isn't in that segment */
srand(rtc_ticks());
if (gint[HWCALC] == HWCALC_FXCG50 && top_addr != 0x8C000000)
{
static kmalloc_arena_t extRAM = { 0 };
extRAM.name = "extram";
extRAM.is_default = true;
extRAM.start = (void*) 0x8c200000;
extRAM.end = (void*) 0x8c500000; // 4MB
extRAM.start = (void*) 0x8C200000;
extRAM.end = (void*) (0x8C200000 + (4 MB));
dclear(C_GREEN);
dprint(1, 11, C_WHITE, "%p-%p of extmem", extRAM.start, extRAM.end);
dupdate();
getkey();
kmalloc_init_arena(&extRAM, true);
kmalloc_add_arena(&extRAM);
dclear(C_GREEN);
dtext(1, 1, C_WHITE, "Ma's Trix will take extra memory.");
dupdate();
getkey();
clock_set_speed(CLOCK_SPEED_F5);
}
dclear(C_GREEN);
dtext(1, 11, C_WHITE, "UTF-8: Mémé a mangé des pommes, des belles pommes ! àè");
dupdate();
getkey();
dclear(C_WHITE);
dtext(1, 1, C_BLACK, "Waiting for USB link...");
dupdate();
usb_init(&out, &in);
usb_init(&key, &key); /* using key as a trash var here */
story_of_usb_comm(); /* Greet the user over USB */
dbg_init();
/* Set UI up... */
ui = ui_create_session();
ui->user = matrix_load();
screen = ui_new_screen(ui_infos_init, NULL, NULL);
ui_add_screen(ui, screen);
screen = ui_new_screen(ui_login_init, ui_login_focus, ui_login_event);
ui_add_screen(ui, screen);
if (ui->user)
{
matrix_initial_sync(ui->user);
screen = ui_new_screen(ui_rooms_init, ui_rooms_focus, ui_rooms_event);
ui_add_screen(ui, screen);
}
else
{
screen = ui_new_screen(ui_login_init, ui_login_focus, ui_login_event);
ui_add_screen(ui, screen);
}
/* Start the UI loop */
ui_session_loop(ui);
end:
/* End everything... */
dbg_end();
usb_close();

44
src/memory.c Normal file
View File

@ -0,0 +1,44 @@
#include <gint/kmalloc.h>
#include <stdint.h>
#include <string.h>
#include <log.h>
#define ARENA_COUNT 4
static char const *arenas[ARENA_COUNT] = { "_uram", "_ostk", "_os", "extram" };
uint32_t utils_total_mem(void)
{
uint32_t mem = 0;
size_t i;
kmalloc_arena_t *arena;
for (i = 0; i < ARENA_COUNT; i++)
{
arena = kmalloc_get_arena(arenas[i]);
if (!strcmp(arenas[i], "_os"))
{
/* The fx-CG50 has 128KB of OS-provided heap. */
mem += 128 * 1024;
break;
}
mem += (uint32_t) (arena->end - arena->start);
}
return mem;
}
uint32_t utils_used_mem(void)
{
uint32_t mem = 0;
size_t i;
kmalloc_arena_t *arena;
kmalloc_gint_stats_t *stats;
for (i = 0; i < ARENA_COUNT; i++)
{
arena = kmalloc_get_arena(arenas[i]);
stats = kmalloc_get_gint_stats(arena);
if (stats)
{
mem += stats->used_memory;
}
}
return mem;
}

157
src/mxc.c
View File

@ -1,7 +1,11 @@
#include "utils.h"
#include <matrix.h>
#include <string.h>
#include <stdlib.h>
#include <http.h>
#include <log.h>
#include <ui.h>
char *matrix_send_file(m_user_t *u, char *f, char *ct)
{
@ -33,11 +37,9 @@ char *matrix_send_file(m_user_t *u, char *f, char *ct)
if (http_transfer_code(trans) != 200)
{
/* TODO */
log_text("%s: code=%d!", __func__, http_transfer_code(trans));
http_transfer_free(trans);
return NULL;
}
log_text("%s: OK!", __func__);
data = http_get_reply_data(trans, &len);
json = utils_json_parse(data, len);
@ -51,3 +53,152 @@ char *matrix_send_file(m_user_t *u, char *f, char *ct)
return mxc;
}
char *matrix_get_file(m_user_t *u, char *mxc, size_t *len)
{
char *path, *pathcpy;
char *cpy, *ret;
http_transfer_t *trans;
if (!u || !mxc || !len)
{
return NULL;
}
if (strncmp(mxc, "mxc://", 6))
{
return NULL;
}
path = utils_strcat("/_matrix/media/v3/download/", mxc + 6);
pathcpy = path;
path = utils_strcat(path, "?timeout_ms=1000");
free(pathcpy);
trans = http_transfer_create(HTTP_GET, u->server, path);
free(path);
http_transfer_send(trans);
if (http_transfer_code(trans) != 200)
{
http_transfer_free(trans);
return NULL;
}
cpy = http_get_reply_data(trans, len);
ret = malloc(*len);
memcpy(ret, cpy, *len);
http_transfer_free(trans);
return ret;
}
char *matrix_get_mxc_avatar(m_user_t *u, char *mxc, size_t *len, int w)
{
char *path, *cpy, *ret;
http_transfer_t *trans;
if (!u || !mxc || !len)
{
return NULL;
}
if (strncmp(mxc, "mxc://", 6))
{
return NULL;
}
path = utils_sprintf(
"/_matrix/media/v3/thumbnail/%s"
"?timeout_ms=1000"
"&width=%d&height=%d",
mxc + 6, w, w
);
trans = http_transfer_create(HTTP_GET, u->server, path);
free(path);
http_transfer_send(trans);
if (http_transfer_code(trans) != 200)
{
http_transfer_free(trans);
return NULL;
}
cpy = http_get_reply_data(trans, len);
ret = malloc(*len);
memcpy(ret, cpy, *len);
http_transfer_free(trans);
return ret;
}
void *matrix_image_from_mxc(void *parent, m_user_t *user, char *mxc, int s)
{
char *data;
size_t size;
img_t oimg;
img_t simg, *ref;
if (!parent || !user || !mxc)
{
return NULL;
}
if ((ref = utils_hashmap_get(user->images, mxc)))
{
simg = *ref;
return jmimage_create(parent, simg);
}
data = matrix_get_mxc_avatar(user, mxc, &size, s);
if (data)
{
oimg = load_png(data, size);
if (img_null(oimg))
{
return NULL;
}
free(data);
ref = malloc(sizeof(img_t));
memcpy(ref, &oimg, sizeof(img_t));
utils_hashmap_set(user->images, mxc, ref);
return jmimage_create(parent, oimg);
}
return NULL;
}
void *matrix_image_from_user(void *parent, m_user_t *user, char *id)
{
char *mxc;
if (!parent || !user || !id)
{
return NULL;
}
if ((mxc = utils_hashmap_get(user->mxcs, id)))
{
return matrix_image_from_mxc(parent, user, mxc, 32);
}
mxc = matrix_get_lavatar(user, id);
utils_hashmap_set(user->mxcs, id, mxc);
return matrix_image_from_mxc(parent, user, mxc, 32);
}
void *matrix_image_from_room_user(void *p, m_user_t *u, m_room_t *r, char *id)
{
hashmap_t *member_state;
char *avatar_url;
jmimage *img;
if (!p || !u || !r || !id)
{
return NULL;
}
member_state = matrix_state_fetch(u, r, "m.room.member", id);
if (!member_state)
{
return matrix_image_from_user(p, u, id);
}
avatar_url = utils_json_as_string(
utils_hashmap_get(member_state, "avatar_url")
);
if (!avatar_url)
{
utils_free_json(member_state);
return matrix_image_from_user(p, u, id);
}
img = matrix_image_from_mxc(p, u, avatar_url, 32);
utils_free_json(member_state);
return img;
}

1
src/pc_brawl/README Normal file
View File

@ -0,0 +1 @@
Ha! Gottem! *That* was the __REAL__ prank here!

View File

@ -21,15 +21,14 @@
"}"
#else
#define FILTER_STRING "{" \
"\"types\":[\"m.room.message\"]" \
"\"types\":[\"m.room.message\",\"m.sticker\",\"m.room.member\",\"m.reaction\"]," \
"\"lazy_load_members\":true" \
"}"
#endif
hashmap_t *
matrix_state_fetch(m_user_t *user, m_room_t *room, char *type, char *key)
{
m_event_t *e;
http_transfer_t *trans;
char *path = NULL, *path_cpy = NULL;
@ -77,6 +76,27 @@ matrix_state_fetch(m_user_t *user, m_room_t *room, char *type, char *key)
return json;
}
char *matrix_resolve_avatar(m_user_t *user, m_room_t *room)
{
char *name;
hashmap_t *name_event;
if (!room || !user)
{
return NULL;
}
name_event = matrix_state_fetch(user, room, "m.room.avatar", "");
if (!name_event)
{
return NULL;
}
name = utils_json_as_string(utils_hashmap_get(name_event, "url"));
name = utils_strcpy(name);
utils_free_json(name_event);
return name;
}
char *matrix_resolve_name(m_user_t *user, m_room_t *room)
{
char *name;
@ -212,6 +232,8 @@ bool matrix_update_room_history(m_user_t *user, m_room_t *room, m_event_t *e, in
m_event_t *event = matrix_parse_event(event_object, true);
char *to_event;
if (is_in_timeline(room, event->event_id) != -1)
{
matrix_free_event(event);
@ -219,7 +241,15 @@ bool matrix_update_room_history(m_user_t *user, m_room_t *room, m_event_t *e, in
}
changed = true;
utils_array_add(room->timeline, event);
/* Look for relations. If there is, add to relation map */
if (!(to_event = matrix_get_relation_source(event)))
{
continue;
}
matrix_add_relation(to_event, room, event);
}
events_after = utils_json_as_array(
@ -252,13 +282,10 @@ matrix_send_event(m_user_t *user, m_room_t *room, char *type, hashmap_t *obj)
char *path = NULL, *path_cpy = NULL;
char *data;
size_t len;
size_t i, size;
http_transfer_t *trans;
hashmap_t *reply;
if (!user || !room || !type || !obj)
{
return;
@ -316,3 +343,328 @@ char *matrix_resolve_nick(m_user_t *user, char *id, m_room_t *room)
utils_free_json(name_event);
return name;
}
static m_room_chunk_t *parse_chunk(hashmap_t *json)
{
#define def_val(n, t, v) json_value_t *n##_val = NULL; \
t n = v
#define get(name, r, as, c) name##_val = utils_hashmap_get(json, #name); \
if (!name##_val && r) \
{ \
char *key; \
void *value; \
while (utils_hashmap_list(json, &key, &value))\
{ \
} \
goto fail; \
} \
else if (name##_val) \
{ \
name = c(utils_json_as_##as(name##_val)); \
}
#define end_val(v, f) if (v) \
{ \
f(v); \
}
#define identity(x) (x)
m_room_chunk_t *chunk;
if (!json)
{
return NULL;
}
def_val(avatar_url, char *, NULL);
def_val(canonical_alias, char *, NULL);
def_val(join_rule, char *, NULL);
def_val(name, char *, NULL);
def_val(room_id, char *, NULL);
def_val(room_type, char *, NULL);
def_val(topic, char *, NULL);
def_val(guest_can_join, bool, false);
def_val(world_readable, bool, false);
def_val(num_joined_members, int, 0);
get(avatar_url, false, string, utils_strcpy);
get(canonical_alias, false, string, utils_strcpy);
get(join_rule, false, string, utils_strcpy);
get(name, false, string, utils_strcpy);
get(room_id, true, string, utils_strcpy);
get(room_type, false, string, utils_strcpy);
get(topic, false, string, utils_strcpy);
get(num_joined_members, true, number, identity);
get(guest_can_join, true, boolean, identity);
get(world_readable, true, boolean, identity);
chunk = malloc(sizeof(*chunk));
chunk->avatar_url = avatar_url;
chunk->canonical_alias = canonical_alias;
chunk->join_rule = join_rule;
chunk->name = name;
chunk->room_id = room_id;
chunk->room_type = room_type;
chunk->topic = topic;
chunk->guest_can_join = guest_can_join;
chunk->world_readable = world_readable;
chunk->num_joined_members = num_joined_members;
return chunk;
#undef define_val
#undef get
#undef identity
fail:
end_val(avatar_url, free);
end_val(canonical_alias, free);
end_val(join_rule, free);
end_val(name, free);
end_val(room_id, free);
end_val(room_type, free);
end_val(topic, free);
return NULL;
#undef end_val
}
/* TODO: Add filter */
array_t *matrix_get_directory(m_user_t *user, char *server, int limit)
{
array_t *dir;
http_transfer_t *trans;
hashmap_t *reply;
array_t *chunks;
size_t i;
char *path;
if (!user || limit == 0)
{
return NULL;
}
path = utils_sprintf(
"/_matrix/client/v3/publicRooms"
"%s%s" "%s%d" ,
server ? "?server=" : "",
server ? server : "",
server ? "&limit=" : "?limit=", limit
);
trans = http_transfer_create(HTTP_GET, user->server, path);
matrix_set_token(trans, user);
free(path);
http_transfer_send(trans);
if (http_transfer_code(trans) != 200)
{
http_transfer_free(trans);
return NULL;
}
reply = http_get_reply_json(trans);
chunks = utils_json_as_array(utils_hashmap_get(reply, "chunk"));
dir = utils_new_array();
for (i = 0; i < utils_array_size(chunks); i++)
{
json_value_t *v = utils_array_get(chunks, i);
hashmap_t *rawc = utils_json_as_object(v);
m_room_chunk_t *chunk = parse_chunk(rawc);
utils_array_add(dir, chunk);
}
utils_free_json(reply);
http_transfer_free(trans);
return dir;
}
void matrix_free_directory(array_t *dir)
{
/* TODO */
(void) dir;
}
bool matrix_state_set(m_user_t *user, m_room_t *room, char *type, char *key, hashmap_t *h)
{
http_transfer_t *trans;
char *path = NULL, *path_cpy = NULL;
if (!user || !room || !type || !key || !h)
{
return false;
}
concat("/_matrix/client/v3/rooms/");
concat(room->id);
concat("/state/");
concat(type);
concat("/");
concat(key);
trans = http_transfer_create(HTTP_PUT, user->server, path);
matrix_set_token(trans, user);
free(path);
http_transfer_set_json(trans, utils_json_copy(h));
http_transfer_send(trans);
if (http_transfer_code(trans) != 200)
{
http_transfer_free(trans);
return false;
}
http_transfer_free(trans);
return true;
}
bool matrix_set_nick(m_user_t *user, m_room_t *room, char *nick)
{
bool ret;
hashmap_t *hm;
if (!user || !room || !nick)
{
return false;
}
hm = utils_new_hashmap();
utils_hashmap_set(hm, "membership", utils_string_as_json(utils_strcpy("join")));
utils_hashmap_set(hm, "displayname", utils_string_as_json(utils_strcpy(nick)));
ret = matrix_state_set(
user, room,
"m.room.member", user->id, hm
);
utils_free_json(hm);
utils_hashmap_set(room->nicks, user->id, utils_strcpy(nick));
return ret;
}
void matrix_redact(m_user_t *user, m_event_t *event, char *reason)
{
http_transfer_t *trans;
hashmap_t *req;
char *path;
uint32_t txn = rtc_ticks();
size_t length;
if (!user || !event)
{
return;
}
length = snprintf(
NULL, 0,
"/_matrix/client/v3/rooms"
"/%s/redact/%s/%d",
event->room_id, event->event_id, txn
);
path = malloc(length + 1);
snprintf(
path, length + 1,
"/_matrix/client/v3/rooms"
"/%s/redact/%s/%d",
event->room_id, event->event_id, txn
);
trans = http_transfer_create(HTTP_PUT, user->server, path);
req = utils_new_hashmap();
if (reason)
{
utils_hashmap_add(
req,
"reason", utils_string_as_json(utils_strcpy(reason))
);
}
http_transfer_set_json(trans, req);
matrix_set_token(trans, user);
free(path);
http_transfer_send(trans);
if (http_transfer_code(trans) == 200)
{
event->redacted = true;
}
http_transfer_free(trans);
}
m_room_t * matrix_join_room(m_user_t *user, char *id)
{
http_transfer_t *transfer;
hashmap_t *request;
m_room_t *ret;
char *path;
if (!user || !id)
{
return NULL;
}
request = utils_new_hashmap();
path = utils_sprintf("/_matrix/client/v3/rooms/%s/join", id);
transfer = http_transfer_create(HTTP_POST, user->server, path);
matrix_set_token(transfer, user);
http_transfer_set_json(transfer, request);
free(path);
http_transfer_send(transfer);
if (http_transfer_code(transfer) != 200)
{
http_transfer_free(transfer);
return NULL;
}
http_transfer_free(transfer);
ret = malloc(sizeof(*ret));
ret->id = utils_strcpy(id);
ret->joined = 0;
ret->nicks = utils_new_hashmap();
ret->relations = utils_new_hashmap();
ret->timeline = utils_new_array();
utils_hashmap_add(user->rooms, id, ret);
matrix_populate_messages(user, ret, 10);
return ret;
}
void matrix_populate_messages(m_user_t *user, m_room_t *room, int limit)
{
http_transfer_t *trans;
hashmap_t *reply;
json_value_t *value;
array_t *chunk;
char *path;
size_t i;
if (!user || !room || !limit)
{
return;
}
path = utils_sprintf(
"/_matrix/client/v3/rooms/%s/messages"
"?limit=%d" "&filter=%s" "&dir=b",
room->id,
limit, FILTER_STRING
);
trans = http_transfer_create(HTTP_GET, user->server, path);
matrix_set_token(trans, user);
http_transfer_send(trans);
free(path);
if (http_transfer_code(trans) != 200)
{
http_transfer_free(trans);
return;
}
reply = http_get_reply_json(trans);
http_transfer_free(trans);
value = utils_hashmap_get(reply, "chunk");
chunk = utils_json_as_array(value);
for (i = 0; i < utils_array_size(chunk); i++)
{
json_value_t *ev = utils_array_get(chunk, i);
hashmap_t *raw_e = utils_json_as_object(ev );
m_event_t *event = matrix_parse_event(raw_e, false);
char *to_event;
utils_array_add(room->timeline, event);
/* Look for relations. If there is, add to relation map */
if (!(to_event = matrix_get_relation_source(event)))
{
continue;
}
matrix_add_relation(to_event, room, event);
}
utils_free_json(reply);
}

View File

@ -1,131 +0,0 @@
#include <stdref.h>
#define isspace(c) ((c)==0x20 || ((c)>=0x09 && (c)<=0x0D))
/**
* refnull()
*
* Tests if a reference points to a valid memory area.
*
* @param ref Reference to test.
*
* @return 1 if the reference is invalid or null, 0 otherwise.
*/
int refnull(reference_t ref)
{
// Directly returning 1 if the reference is null, 0 otherwise.
return (ref.begin >= ref.end);
}
/**
* refxtrct()
*
* Extracts the data pointed by a reference and returns it as a
* dynamically-allocated text pointer. The text is NUL-ended.
*/
char *refxtrct(reference_t ref)
{
// Storing the reference length in a variable.
const int length = ref.end - ref.begin;
// Using a text pointer.
char *text;
// Allocating memory.
text = (char *)malloc(length + 1);
// Handling alloc failure.
if(!text) return NULL;
// Copying the data.
memcpy(text, ref.begin, length);
// Setting the NUL ending character.
text[length] = 0;
// Returning the text.
return text;
}
/**
* reftrim()
*
* Reduces the given reference by excluding the beginning and ending
* spacing.
*
* @param ref Original reference.
*
* @return Trimmed reference.
*/
reference_t reftrim(reference_t ref)
{
// Reducing the end address until a non-space charater is reached.
while(ref.end > ref.begin && isspace(*(ref.end-1))) ref.end--;
// If the reference has become empty, returning it.
if(ref.end == ref.begin) return ref;
// Now that we ensured the reference contains non-space characters,
// just going forward from the beginning.
while(isspace(*ref.begin)) ref.begin++;
// Returning the obtained reference.
return ref;
}
/**
* refscmp()
*
* Compares a reference with a string. Returns the result of strncmp()
* comparison, and -1 whenever the arguments are not valid or the lengths
* don't match.
*
* @param ref Reference to compare to.
* @param str String to compare with.
*
* @return Result of strncmp() comparison, or -1 on error.
*/
int refscmp(reference_t ref, const char *str)
{
// Using a variable to store the comparing length.
int length;
// Testing arguments validity.
if(refnull(ref) || !str) return -1;
// Getting the wanted length.
length = strlen(str);
// Returning 0 if the string length does not match the reference's.
if(ref.end - ref.begin != length) return -1;
// Returning the result of strncmp() comparison.
return strncmp(ref.begin, str, length);
}
/**
* refs,cmp()
*
* Compares a reference with a string in the limit of `length` characters.
*
* @param ref Reference to compare to.
* @param str String to compare with.
* @param length Maximum number of characters to compare.
*
* @return Result of strncmp() comparison, or -1 on error.
*/
int refsncmp(reference_t ref, const char *str, int length)
{
// Testing arguments validity.
if(refnull(ref) || !str || length <= 0) return -1;
// Returning the result of strncmp() comparison.
return strncmp(ref.begin, str, length);
}

View File

@ -1,8 +1,10 @@
#include <utils.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <log.h>
char *utils_strcpy(char *in)
{
@ -74,3 +76,90 @@ char * utils_basename(char *str)
}
return utils_strcpy(last_occurence);
}
int utils_htov(char c)
{
if (c >= '0' && c <= '9')
{
return c - '0';
}
if (c >= 'a' && c <= 'f')
{
return (c - 'a') + 0xa;
}
if (c >= 'A' && c <= 'F')
{
return (c - 'A') + 0xA;
}
return 0x0;
}
char *utils_unitoutf(uint16_t uni)
{
char *ret;
if (uni == 0x0000)
{
/* Don't even *try* putting a NULL byte */
return NULL;
}
if (uni <= 0x007F)
{
ret = malloc(2 * sizeof(char));
ret[0] = uni & 0x00FF;
ret[1] = '\0';
return ret;
}
if (uni >= 0x0080 && uni <= 0x07FF)
{
ret = malloc(3 * sizeof(char));
ret[0] =
0b11000000 |
((uni & 0x0F00) >> 6) |
((uni & 0x00F0) >> 6);
ret[1] =
0b10000000 |
((uni & 0x00F0) & 0b110000) |
(uni & 0x000F);
ret[2] = '\0';
return ret;
}
if (uni >= 0x0800)
{
ret = malloc(4 * sizeof(char));
ret[0] =
0b11100000 |
((uni & 0xF000) >> 12);
ret[1] =
0b10000000 |
((uni & 0x0F00) >> 6) |
((uni & 0x00F0) >> (4 + 2));
ret[2] =
0b10000000 |
((uni & 0x00F0) & 0b110000) |
((uni & 0x000F));
ret[3] = '\0';
return ret;
}
return NULL;
}
char * utils_sprintf(char *fmt, ...)
{
va_list list;
char *str;
size_t len;
if (!fmt)
{
return NULL;
}
va_start(list, fmt);
len = vsnprintf(NULL, 0, fmt, list);
str = malloc(len + 1);
vsnprintf(str, len + 1, fmt, list);
va_end(list);
return str;
}

34
src/strongest.c Normal file
View File

@ -0,0 +1,34 @@
#include <easters.h>
#include <gint/defs/timeout.h>
#include <gint/keyboard.h>
#include <gint/display.h>
extern bopti_image_t cirno;
extern font_t curse_casual;
void easter_strongest(void)
{
dfont(&curse_casual);
timeout_t tm;
size_t i = 0;
while (true)
{
dclear(C_BLACK);
dimage(0, 0, &cirno);
if (i % 2 == 0)
{
dtext_opt(
DWIDTH / 2, DHEIGHT / 2,
C_RGB(31, 31, 0), C_NONE,
DTEXT_CENTER, DTEXT_MIDDLE,
"STRONGEST", 9
);
}
dupdate();
tm = timeout_make_ms(1000);
while (!timeout_elapsed(&tm));
i++;
}
dfont(NULL);
}

View File

@ -9,10 +9,9 @@
#include <utils.h>
#include <http.h>
#include <log.h>
/* I hate this. */
#define FILTER_STRING "{\"event_fields\":[\"content.body\",\"content.msgtype\",\"event_id\",\"sender\",\"origin_server_ts\",\"type\"],\"event_format\":\"client\",\"presence\":{\"limit\":1,\"not_types\":[\"*\"]},\"account_data\":{\"limit\":1},\"room\":{\"ephemeral\":{\"limit\":1,\"not_types\":[\"m.receipt\"]},\"state\":{\"not_types\":[\"m.room.member\",\"m.room.server_acl\"],\"lazy_load_members\":true,\"limit\":1},\"timeline\":{\"limit\":1,\"not_types\":[\"m.room.server_acl\"],\"types\":[\"m.room.message\"],\"lazy_load_members\":true,\"unread_thread_notifications\":true},\"account_data\":{\"limit\":1}}}"
#define FILTER_STRING "{\"event_fields\":[\"content.body\",\"content.msgtype\",\"room_id\",\"event_id\",\"sender\",\"origin_server_ts\",\"type\"],\"event_format\":\"client\",\"presence\":{\"limit\":1,\"not_types\":[\"*\"]},\"account_data\":{\"limit\":1},\"room\":{\"ephemeral\":{\"limit\":1,\"not_types\":[\"m.receipt\"]},\"state\":{\"not_types\":[\"m.room.member\",\"m.room.server_acl\"],\"lazy_load_members\":true,\"limit\":1},\"timeline\":{\"limit\":1,\"not_types\":[\"m.room.server_acl\"],\"types\":[\"m.room.message\"],\"lazy_load_members\":true,\"unread_thread_notifications\":true},\"account_data\":{\"limit\":1}}}"
/* TODO: This will not be fun on Conduit/Dendrite.
* It seems like both of them have a tendency to *ignore* some properties I
@ -57,7 +56,7 @@ void matrix_initial_sync(m_user_t *user)
if (http_transfer_code(trans) != 200)
{
const char *msg = "Reply isn't 200.\n";
/* TODO: Error managment */
}
json = utils_json_parse(reply, length);
@ -100,6 +99,7 @@ void matrix_initial_sync(m_user_t *user)
room->id = utils_strcpy(key);
room->timeline = utils_new_array();
room->relations = utils_new_hashmap();
room->nicks = utils_new_hashmap();
/* TODO: Create "add event to timeline" function */
@ -135,10 +135,16 @@ void matrix_initial_sync(m_user_t *user)
if (event)
{
json_value_t *msg;
msg = utils_hashmap_get(event->content, "body");
char *to_event;
utils_array_add(room->timeline, event);
continue;
/* Look for relations. If there is, add to relation map */
if (!(to_event = matrix_get_relation_source(event)))
{
continue;
}
matrix_add_relation(to_event, room, event);
}
}

View File

@ -111,6 +111,8 @@ ui_screen_t * ui_set_screen(ui_session_t *ui, size_t idx)
return indexed;
}
#include <log.h>
bool ui_manage_event(ui_session_t *ui, jevent e)
{
ui_screen_t *screen;
@ -133,8 +135,10 @@ bool ui_manage_event(ui_session_t *ui, jevent e)
widget = screen->on_event(screen, e);
if (widget)
{
/* This lil' fella has an unusual tendency to crash. */
/* TODO: Actually investigate on this arsehole */
log_text("focusing (%p, %p)", ui->justui_screen, widget);
jscene_set_focused_widget(ui->justui_screen, widget);
log_text("focused");
return true;
}
return false;
@ -142,7 +146,7 @@ bool ui_manage_event(ui_session_t *ui, jevent e)
void ui_session_loop(ui_session_t *ui)
{
int key = 0;
while (key != KEY_EXIT)
while (true)
{
jevent e = jscene_run(ui->justui_screen);
@ -239,40 +243,3 @@ jpainted *ui_image(void *parent, const bopti_image_t *img)
return ret;
}
#define SCALE 20
static void draw_blur(int x, int y, char *bh)
{
int w = 0, h = 0;
size_t xp, yp;
uint16_t *colors = utils_decode_blurhash(bh, &w, &h);
for (yp = 0; yp < h; yp++)
{
for (xp = 0; xp < w; xp++)
{
uint16_t c = colors[yp * w + xp];
drect(
xp * SCALE + x, yp * SCALE + y,
xp * SCALE + SCALE, yp * SCALE + SCALE,
c
);
}
}
free(colors);
}
jpainted *ui_blurhash(void *p, char *bh)
{
int w = 0, h = 0;
uint16_t *nothing;
nothing = utils_decode_blurhash(bh, &w, &h);
free(nothing);
return jpainted_create(
draw_blur,
(j_arg_t) (void *) bh,
w * SCALE, h * SCALE,
p
);
}

193
src/ui_accinfo.c Normal file
View File

@ -0,0 +1,193 @@
#include <ui.h>
#include <utils.h>
#include <ui_generators.h>
#include <justui/jwidget.h>
#include <justui/jlabel.h>
#include <justui/jframe.h>
#include <justui/jfkeys.h>
#include <justui/jinput.h>
#include <libimg.h>
#include <stdlib.h>
#include <string.h>
#include <easters.h>
#include <matrix.h>
#include <log.h>
extern font_t terminus_strong;
typedef struct accinfo_extra {
jwidget *main;
jlabel *acct_name;
void *content;
void *input;
void *input_widget;
} accinfo_extra_t;
void render(int x, int y, img_t *img)
{
img_render_vram(*img, x, y);
}
void * ui_accinfo_init(ui_screen_t *that)
{
#define display(un, property)do {\
jwidget *wid = jwidget_create(cwidget);\
jlabel *left, *right; \
jlayout_set_hbox(wid)->spacing = 1; \
jwidget_set_stretch(wid, 1, 0, false); \
left = jlabel_create(un, wid); \
right = jlabel_create("[unknown]", wid); \
jwidget_set_stretch(left, 1, 0, false); \
jlabel_asprintf(right, \
"%s", \
that->owner->user->property \
); \
} while(0)
jlabel *title;
jwidget *basic_acct;
jlabel *acct_name;
jframe *content;
jwidget *cwidget;
jwidget *widget;
jfkeys *jfk;
accinfo_extra_t *extra = malloc(sizeof(accinfo_extra_t));
widget = jwidget_create(that->owner->stack);
jlayout_set_vbox(widget)->spacing = 1;
jwidget_set_padding(widget, 0, 0, 0, 0);
jwidget_set_stretch(widget, 1, 1, false);
title = jlabel_create("Account information", widget);
jwidget_set_padding(title, 1, 1, 3, 1);
jwidget_set_stretch(title, 1, 0, false);
jwidget_set_background(title, C_BLACK);
jlabel_set_text_color(title, C_WHITE);
basic_acct = jwidget_create(widget);
jwidget_set_padding(basic_acct, 1, 1, 1, 1);
jwidget_set_stretch(basic_acct, 1, 0, false);
jlayout_set_hbox(basic_acct)->spacing = 1;
jwidget_set_border(basic_acct, J_BORDER_SOLID, 1, C_BLACK);
{
char *avatar_url = matrix_get_avatar(that->owner->user);
matrix_image_from_mxc(basic_acct, that->owner->user, avatar_url, 32);
}
{
char *acc_name = matrix_get_displayname(that->owner->user);
if (!acc_name)
{
acc_name = utils_strcpy(that->owner->user->id);
}
acct_name = jlabel_create("", basic_acct);
extra->acct_name = acct_name;
jlabel_asprintf(acct_name, "%s", acc_name);
jlabel_set_font(acct_name, &terminus_strong);
free(acc_name);
}
content = jframe_create(widget);
jwidget_set_padding(content, 2, 2, 2, 2);
jwidget_set_stretch(content, 1, 1, false);
jframe_set_keyboard_control(content, true);
cwidget = jwidget_create(content);
jlayout_set_vbox(cwidget)->spacing = 1;
jwidget_set_padding(cwidget, 2, 2, 2, 2);
jwidget_set_stretch(cwidget, 1, 1, false);
jwidget_set_maximum_width(cwidget, DWIDTH - 5);
jfk = jfkeys_create("#USERNAME;;;;;;#LOGOUT", widget);
jwidget_set_stretch(jfk, 1, 0, false);
jlabel_create("General account information", cwidget);
display("User ID ", id);
display("Device ID ", device_id);
display("Server ", server);
display("Access token ", access_token);
/* TODO: Device list */
extra->content = content;
extra->main = widget;
extra->input_widget = NULL;
extra->input = NULL;
that->data = extra;
return content;
}
void * ui_accinfo_focus(ui_screen_t *that)
{
(void) that;
return NULL;
}
void * ui_accinfo_event(ui_screen_t *that, jevent e)
{
key_event_t ke = e.key;
accinfo_extra_t *extra = that->data;
if (e.type == JINPUT_VALIDATED)
{
jinput *input = extra->input;
char *text = (char *) jinput_value(input);
matrix_set_displayname(that->owner->user, text);
jlabel_asprintf(extra->acct_name, "%s", text);
jwidget_destroy(extra->input_widget);
extra->input_widget = NULL;
extra->input = NULL;
if (!strcmp(text, "Cirno"))
{
easter_strongest();
}
return extra->content;
}
if (ke.key == KEY_F1 && ke.type == KEYEV_UP && !extra->input)
{
jwidget *floating = jwidget_create(extra->main);
jinput *input;
jwidget_set_floating(floating, true);
jlayout_set_vbox(floating)->spacing = 3;
jwidget_set_border(floating, J_BORDER_SOLID, 1, C_BLACK);
jwidget_set_background(floating, C_WHITE);
jwidget_set_fixed_width(floating, DWIDTH - 70);
jwidget_set_fixed_height(floating, DHEIGHT - 150);
floating->x = ((DWIDTH - floating->max_w) / 2);
floating->y = ((DHEIGHT - floating->max_h) / 2);
jlabel_create("Enter a username", floating);
input = jinput_create("", 32, floating);
jwidget_set_stretch(input, 1, 0, false);
jwidget_set_padding(input, 1, 2, 1, 2);
extra->input = input;
extra->input_widget = floating;
return input;
}
if (ke.key == KEY_EXIT)
{
jwidget_destroy(extra->main);
free(that->data);
ui_destroy_screen(that->owner);
return NULL;
}
if (ke.key == KEY_F6 && ke.type == KEYEV_UP && !extra->input)
{
matrix_logout(that->owner->user);
that->owner->user = NULL;
exit(0); /* TODO: gracefully exit */
}
return NULL;
}

View File

@ -1,5 +1,7 @@
#include <ui.h>
#include <gint/defs/timeout.h>
#include <gint/clock.h>
#include <gint/usb-ff-bulk.h>
#include <gint/usb.h>
#include <gint/defs/timeout.h>
@ -11,6 +13,7 @@
#include <unistd.h>
#include <easters.h>
#include <matrix.h>
#include <utils.h>
#include <info.h>
@ -19,14 +22,15 @@ const char *infos =
"= Ma's Trix\n"
"A half decent [matrix] client.\n\n\n"
"LICENSE: \n"
"- CeCILL\n\n"
"- MIT (see COPYING and LAWYER STUFF)\n\n"
"WRITTEN BY: \n"
"- LDA <@lda:a.freetards.xyz>\n"
"(pssht KONTRIBUTORS, put your contacts here!)\n\n"
"THANKS TO: \n"
"- Lephe for writing gint, fxlink, libimg, WebCalc and other tools\n"
"- Lephe for writing gint, fxlink, libimg, uf5x7/uf8x9 and other tools\n"
"- Yoran Heling for making yxml\n"
"- ari.lt for offering the mastrix.org domain for free!\n"
"- The [matrix] Foundation for providing its specification to "
"the public\n"
@ -34,31 +38,107 @@ const char *infos =
"wasnt supposed to when he did it since i didnt release it\n"
"- Various other open-source contributors "
"for additional software used to create Ma's Trix\n"
"- @moonzero:4d2.org and @sarah:4d2.org for finding a small bug " "\n"
"in my JSON implementation without even trying to (gg)" "\n"
"- plate, just come back please :(\n\n"
"FUN STUFF (press F1 to exit): \n"
"FUN STUFF (press EXIT to exit): \n"
"- Program name: " MASTRIX_NAME "\n"
"- Program version: " MASTRIX_VERS "\n"
"- Intended platform: " MASTRIX_PLAT "\n"
"- User-Agent string: " MASTRIX_UA "\n";
"- User-Agent string: " MASTRIX_UA "\n\n"
"LAWYER STUFF (not fun): \n"
"- YXML (MIT)\n"
"Copyright (c) 2013-2014 Yoran Heling\n\n"
"Permission is hereby granted, free of charge, to any person obtaining\n"
"a copy of this software and associated documentation files (the\n"
"\"Software\"), to deal in the Software without restriction, including\n"
"without limitation the rights to use, copy, modify, merge, publish,\n"
"distribute, sublicense, and/or sell copies of the Software, and to\n"
"permit persons to whom the Software is furnished to do so, subject to\n"
"the following conditions:\n"
"\n"
"The above copyright notice and this permission notice shall be included\n"
"in all copies or substantial portions of the Software.\n"
"\n"
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n"
"EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n"
"MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n"
"IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n"
"CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n"
"TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n"
"SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n"
"- Ma's Trix (MIT)\n"
"Copyright (c) 2024 LDA\n\n"
"Permission is hereby granted, free of charge, to any person obtaining\n"
"a copy of this software and associated documentation files (the\n"
"\"Software\"), to deal in the Software without restriction, including\n"
"without limitation the rights to use, copy, modify, merge, publish,\n"
"distribute, sublicense, and/or sell copies of the Software, and to\n"
"permit persons to whom the Software is furnished to do so, subject to\n"
"the following conditions:\n"
"\n"
"The above copyright notice and this permission notice shall be included\n"
"in all copies or substantial portions of the Software.\n"
"\n"
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n"
"EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n"
"MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n"
"IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n"
"CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n"
"TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n"
"SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n" ;
void * ui_infos_init(ui_screen_t *that)
{
jframe *info_frame;
jlabel *info_label;
info_frame = jframe_create(that->owner->stack);
jframe_set_keyboard_control(info_frame, true);
jwidget_set_padding(info_frame, 1, 1, 1, 1);
jwidget_set_stretch(info_frame, 1, 1, false);
info_label = jlabel_create(infos, info_frame);
jlabel_create(infos, info_frame);
return info_frame;
}
void * ui_infos_event(ui_screen_t *that, jevent e)
{
if (e.key.type == KEYEV_UP && e.key.key == KEY_F1)
if (e.key.type == KEYEV_UP && e.key.key == KEY_EXIT)
{
ui_destroy_screen(that->owner);
}
if (e.key.type == KEYEV_UP && e.key.key == KEY_F1)
{
easter_credit();
}
return NULL;
}
#define TEXT_CNTR 7
static const char *texts[TEXT_CNTR] = {
"It's over.",
"You've reached the credit screen.",
"I hope you're proud.",
"If you wonder what I'm up to",
"I'm on a FOOLS ERRAND",
"SMASHing out those bugs",
"All on a PC"
};
extern bopti_image_t credits_image;
void easter_credit(void)
{
int y = 0;
dimage(0, 0, &credits_image);
for (y = 0; y < TEXT_CNTR; y++)
{
dprint_opt(0, y * 11 + 20, C_WHITE, C_NONE, DTEXT_RIGHT, DTEXT_MIDDLE, texts[y]);
}
dupdate();
sleep_ms(1000);
while (true)
{
if (getkey().key == KEY_F1)
{
break;
}
}
}

View File

@ -37,7 +37,6 @@ void * ui_login_init(ui_screen_t *that)
);
login_data_t *data;
jlabel *error;
jpainted *logo;
login_content = jwidget_create(login_widget);
that->data = malloc(sizeof(login_data_t));
data = that->data;
@ -56,7 +55,7 @@ void * ui_login_init(ui_screen_t *that)
jwidget_set_padding(login_content, 0, 0, 0, 0);
jwidget_set_stretch(login_content, 1, 1,false);
logo = ui_image(
ui_image(
login_content,
&mastrix
);
@ -103,12 +102,10 @@ void * ui_login_event(ui_screen_t *that, jevent e)
if (e.type == JINPUT_VALIDATED)
{
jinput *server, *login, *password;
jinput *server;
jlabel *error = data->error;
server = data->server;
login = data->login;
password = data->password;
if (data->focusing == data->server)
{
char *delegated = NULL;
@ -145,6 +142,7 @@ void * ui_login_event(ui_screen_t *that, jevent e)
break;
case DELEG_FAIL_PROMPT:
jlabel_set_text_color(error, C_RED);
/* Fallthrough */
case DELEG_PROMPT:
jlabel_asprintf(
error,
@ -199,6 +197,7 @@ void * ui_login_event(ui_screen_t *that, jevent e)
/* TODO: Create a function to logout later */
ui_screen_t *rooms;
that->owner->user = user;
matrix_save(user);
jlabel_set_text_color(error, C_GREEN);
jlabel_asprintf(

View File

@ -2,6 +2,7 @@
#include <gint/drivers/keydev.h>
#include <gint/timer.h>
#include <gint/rtc.h>
#include <justui/jfileselect.h>
#include <justui/jwidget.h>
@ -14,9 +15,14 @@
#include <unistd.h>
#include <stdlib.h>
#include <command.h>
#include <easters.h>
#include <matrix.h>
#include <utils.h>
#include <usb.h>
#include <log.h>
extern font_t uf5x7, uf8x9;
typedef struct ui_room_extra {
jwidget *main;
@ -25,12 +31,15 @@ typedef struct ui_room_extra {
jwidget *tl_widget;
jfkeys *jfk;
jmlist *timeline;
jmlist *charpick;
jfileselect *file;
jwidget *time_head;
m_room_t *room;
m_user_t *user;
m_event_t *replying;
int timer_id;
volatile bool flag;
@ -41,8 +50,9 @@ typedef struct ui_room_extra {
static int timer_cb(ui_screen_t *);
#include "keymap_translate.h"
typedef struct item_extra {
const char *code;
ui_screen_t *screen;
m_event_t *event;
@ -51,28 +61,40 @@ typedef struct item_extra {
static void ui_room_act(item_extra_t *ie)
{
ui_room_extra_t *extra = ie->extra;
m_event_t *event = ie->event;
char *msgtype;
char *body;
char *url;
if (strcmp(event->type, "m.room.message"))
{
/* Do not interact over non-messages for now */
return;
}
/* TODO: Act upon the event(downloading/...) */
}
msgtype = utils_json_as_string(
utils_hashmap_get(event->content, "msgtype")
);
if (!strcmp(msgtype, "m.file") || !strcmp(msgtype, "m.image"))
{
char *data;
size_t len = 0;
body = utils_json_as_string(
utils_hashmap_get(event->content, "body")
);
body = utils_strcat("/", body);
url = utils_json_as_string(
utils_hashmap_get(event->content, "url")
);
static void set_chr(jmlist_item *item, int idx)
{
char c = idx + ' ';
jlabel *label;
jwidget_set_stretch(item->widget, 1, 0, false);
jlayout_set_vbox(item->widget)->spacing = 1;
jwidget_set_background(item->widget, C_WHITE);
data = matrix_get_file(ie->extra->user, url, &len);
utils_write_file(body, data, len);
label = jlabel_create("", item->widget);
jlabel_asprintf(label, "%c", c);
jwidget_set_background(label, C_WHITE);
free(data);
free(body);
return;
}
ie->extra->replying = event;
}
static void set(jmlist_item *item, int idx)
{
@ -80,14 +102,9 @@ static void set(jmlist_item *item, int idx)
ui_room_extra_t *extra = ie->extra;
m_event_t *e = ie->event;
jlabel *sender;
jlabel *content;
json_value_t *body;
char *msg;
char *nick;
matrix_show_event(extra->room, extra->user, e, item->widget);
(void) idx;
}
static void screen_free(ui_screen_t *that)
{
@ -161,11 +178,13 @@ void * ui_room_init(ui_screen_t *that)
type_message = jinput_create("Send a message: ", 100, main);
type_message = jinput_create("Type: ", 100, main);
jwidget_set_stretch(type_message, 1, 0, false);
jwidget_set_border(type_message, J_BORDER_SOLID, 1, C_BLACK);
jinput_set_font(type_message, &uf5x7);
jinput_set_keymap_function(type_message, uni_keymap_translate);
jfk = jfkeys_create("@MESG;@ROOM;/FILE;;#CHR;@EXIT", main);
jfk = jfkeys_create(utils_strcpy("@MESG;@ROOM;/FILE;;;@EXIT"), main);
jwidget_set_stretch(jfk, 1, 0, false);
{
@ -178,7 +197,8 @@ void * ui_room_init(ui_screen_t *that)
extra->jfk = jfk;
extra->on_timeline = true;
extra->file = NULL;
extra->charpick = NULL;
extra->replying = NULL;
extra->time_head = NULL;
extra->timer_id = timer_configure(
TIMER_ANY,
@ -193,13 +213,15 @@ void * ui_room_init(ui_screen_t *that)
m_event_t *e = utils_array_get(room->timeline, size - i - 1);
item_extra_t *ie = malloc(sizeof(item_extra_t));
ie->code = "hiiiiiii >:3";
ie->event = e;
ie->screen = that;
ie->extra = extra;
jmlist_add_item(timeline, ie);
}
jmlist_select(timeline, 1);
jmlist_select(
extra->timeline,
utils_array_size(extra->timeline->arr) - 1
);
extra->flag = false;
extra->flag1 = false;
@ -207,9 +229,26 @@ void * ui_room_init(ui_screen_t *that)
return timeline;
}
static ssize_t find_event_in_timeline(jmlist *timeline, char *id)
{
ssize_t i;
ssize_t len = utils_array_size(timeline->arr);
for (i = 0; i < len; i++)
{
jmlist_item *item = utils_array_get(timeline->arr, i);
item_extra_t *extra = item->data;
m_event_t *e = extra->event;
if (!strcmp(e->event_id, id))
{
return i;
}
}
return -1;
}
static void ui_update_history(ui_screen_t *that)
{
/* help */
ui_room_extra_t *extra = that->data;
size_t lidx, i, size;
@ -232,18 +271,36 @@ static void ui_update_history(ui_screen_t *that)
m_event_t *e = utils_array_get(extra->room->timeline, size - i - 1);
item_extra_t *ie = malloc(sizeof(item_extra_t));
ie->code = "hii :3";
ie->event = e;
ie->screen = that;
ie->extra = extra;
/*jmlist_prepend_item(extra->timeline, ie);*/
jmlist_add_item(extra->timeline, ie);
if (matrix_get_relation_type(e))
{
jmlist_item *item;
item_extra_t *itemextra;
int event = find_event_in_timeline(
extra->timeline,
matrix_get_relation_source(e)
);
if (event == -1)
{
log_text("Couldn't find!");
continue;
}
item = utils_array_get(extra->timeline->arr, event);
itemextra = item->data;
jmlist_redo(extra->timeline, event);
log_text("Have to redo(%d->%s)!", event, itemextra->event->event_id);
}
}
jmlist_select(extra->timeline, 1);
return;
}
#include <string.h>
void emit(void *w0, jevent e)
{
J_CAST(w)
@ -316,122 +373,88 @@ static void * ui_file_event(ui_screen_t *that, jevent e)
}
return extra->file;
}
static void * ui_char_event(ui_screen_t *that, jevent e)
static m_event_t * send_msg(ui_room_extra_t *extra)
{
ui_room_extra_t *extra = that->data;
if (e.type == JMLIST_ITEM_CHOSEN)
hashmap_t *msg = utils_new_hashmap();
m_event_t *e;
if (!extra)
{
char c = jmlist_selected(extra->charpick) + ' ';
char cp[2] = { c, '\0' };
char *input = utils_strcat(extra->input->text, cp);
free(extra->input->text);
extra->input->text = input;
extra->input->size++;
extra->input->widget.dirty = 1;
jwidget_destroy(extra->charpick);
extra->charpick = NULL;
return extra->input;
}
return extra->charpick;
}
void * ui_room_event(ui_screen_t *that, jevent e)
{
ui_room_extra_t *extra = that->data;
if (extra->charpick)
{
return ui_char_event(that, e);
}
if (e.type == JMLIST_ITEM_CHOSEN && !extra->file)
{
/* TODO */
int selected = jmlist_selected(extra->timeline);
item_extra_t *ie = utils_array_get(extra->timeline->arr, selected);
if (!ie)
{
return NULL;
}
ui_room_act(ie);
return NULL;
}
if (e.type == JMLIST_ITEM_CHANGE && !extra->file)
utils_hashmap_set(
msg,
"msgtype",
utils_string_as_json(
utils_strcpy("m.text")
)
);
if (extra->replying)
{
int selected = jmlist_selected(extra->timeline);
if (selected == 0)
{
ui_update_history(that);
return extra->timeline;
}
extra->timeline->widget.update = 1;
return NULL;
}
if (e.type == JMLIST_ITEM_NOP && !extra->file)
{
ui_room_extra_t *extra = that->data;
m_event_t *e;
ssize_t size, i;
size_t from;
int prev;
uint8_t code = extra->input->mode;
jrect rect;
e = utils_array_get(extra->room->timeline, 0);
rect = extra->timeline->rect;
prev = jmlist_selected(extra->timeline);
that->owner->waiting = true;
matrix_update_room_history(extra->user, extra->room, e, 10);
from = is_in_timeline(extra->room, e->event_id);
if (from != 0)
{
size = utils_array_size(extra->room->timeline);
for (i = from - 1; i >= 0; i--)
{
m_event_t *e = utils_array_get(
extra->room->timeline,
i
);
item_extra_t *ie = malloc(sizeof(item_extra_t));
ie->code = "hi";
ie->event = e;
ie->screen = that;
ie->extra = that->data;
jmlist_add_item(extra->timeline, ie);
}
}
that->owner->waiting = false;
extra->input->mode = code; /* It seems like the input doesn't keep
its mode, somehow??????? */
((ui_room_extra_t *) that->data)->flag1 = false;
return NULL;
}
if (e.type == JINPUT_VALIDATED && !extra->file)
{
hashmap_t *msg = utils_new_hashmap();
m_event_t *e;
hashmap_t *relations = utils_new_hashmap();
hashmap_t *reply = utils_new_hashmap();
utils_hashmap_set(
reply,
"event_id",
utils_string_as_json(
utils_strcpy(extra->replying->event_id)
)
);
utils_hashmap_set(
relations,
"m.in_reply_to", utils_object_as_json(reply)
);
utils_hashmap_set(
msg,
"body",
utils_string_as_json(
utils_strcpy((char *) jinput_value(extra->input))
)
"m.relates_to", utils_object_as_json(relations)
);
}
utils_hashmap_set(
msg,
"body",
utils_string_as_json(
utils_strcpy((char *) jinput_value(extra->input))
)
);
jinput_clear(extra->input);
matrix_send_event(
extra->user, extra->room,
"m.room.message", msg
);
e = utils_array_get(
extra->room->timeline,
utils_array_size(extra->room->timeline) - 1
);
extra->replying = NULL;
return e;
}
static void manage_command(ui_screen_t *that, command_t *cmd)
{
ui_room_extra_t *extra = that->data;
if (!strcmp(cmd->command, "punch"))
{
/* TODO: Actual punch MSC */
char *punched = utils_array_get(cmd->arguments, 0);
hashmap_t *msg;
m_event_t *e;
if (!punched)
{
return;
}
msg = utils_new_hashmap();
utils_hashmap_set(
msg, "body",
utils_string_as_json(utils_strcat("punches ", punched))
);
utils_hashmap_set(
msg,
"msgtype",
utils_string_as_json(
utils_strcpy("m.text")
)
utils_string_as_json(utils_strcpy("m.emote"))
);
jinput_clear(extra->input);
matrix_send_event(
@ -445,6 +468,268 @@ void * ui_room_event(ui_screen_t *that, jevent e)
that->owner->waiting = true;
matrix_update_room_history(extra->user, extra->room, e, 5);
that->owner->waiting = false;
}
if (!strcmp(cmd->command, "fx"))
{
hashmap_t *msg;
m_event_t *e;
msg = utils_new_hashmap();
utils_hashmap_set(
msg, "body",
utils_string_as_json(utils_strcat("Sent from my ", "fx-CG50"))
);
utils_hashmap_set(
msg,
"msgtype",
utils_string_as_json(utils_strcpy("m.text"))
);
jinput_clear(extra->input);
matrix_send_event(
extra->user, extra->room,
"m.room.message", msg
);
e = utils_array_get(
extra->room->timeline,
utils_array_size(extra->room->timeline) - 1
);
that->owner->waiting = true;
matrix_update_room_history(extra->user, extra->room, e, 5);
that->owner->waiting = false;
}
if (!strcmp(cmd->command, "myroomnick") || !strcmp(cmd->command, "rn"))
{
char *nick = utils_array_get(cmd->arguments, 0);
if (!nick)
{
return;
}
matrix_set_nick(extra->user, extra->room, nick);
}
if (!strcmp(cmd->command, "reply") || !strcmp(cmd->command, "r"))
{
int selected = jmlist_selected(extra->timeline);
jmlist_item *i = utils_array_get(extra->timeline->arr, selected);
item_extra_t *ie = i->data;
m_event_t *e = ie->event;
/* TODO: A 'lil bit of formatting when this is set to anything */
extra->replying = e;
}
if (!strcmp(cmd->command, "delete") || !strcmp(cmd->command, "del"))
{
char *reason = "Requested by user";
int selected = jmlist_selected(extra->timeline);
jmlist_item *i = utils_array_get(extra->timeline->arr, selected);
item_extra_t *ie = i->data;
m_event_t *e = ie->event;
if (utils_array_size(cmd->arguments) > 0)
{
reason = utils_array_get(cmd->arguments, 0);
}
if (strcmp(e->sender, extra->user->id))
{
/* Yes, I know about power levels, but I am NOT
* going to try and manage that */
return;
}
matrix_redact(extra->user, e, reason);
/* Rerender the event, to make sure the user knows it's redacted. */
jmlist_redo(extra->timeline, selected);
}
if (!strcmp(cmd->command, "download") || !strcmp(cmd->command, "dl"))
{
int selected = jmlist_selected(extra->timeline);
jmlist_item *i = utils_array_get(extra->timeline->arr, selected);
item_extra_t *ie = i->data;
m_event_t *e = ie->event;
char *data, *body, *url, *msgtype;
size_t len = 0;
if (strcmp(e->type, "m.room.message"))
{
return;
}
msgtype = utils_json_as_string(
utils_hashmap_get(e->content, "msgtype")
);
if (strcmp(msgtype, "m.file") &&
strcmp(msgtype, "m.image") &&
strcmp(msgtype, "m.audio"))
{
return;
}
body = utils_json_as_string(
utils_hashmap_get(e->content, "body")
);
body = utils_strcat("/", body);
url = utils_json_as_string(
utils_hashmap_get(e->content, "url")
);
data = matrix_get_file(ie->extra->user, url, &len);
utils_write_file(body, data, len);
free(data);
free(body);
}
if (!strcmp(cmd->command, "9") || !strcmp(cmd->command, "cirno"))
{
rtc_time_t time;
rtc_get_time(&time);
if (time.month_day != 9 || time.month != 9)
{
/* This command only works on 9/9/AAAA */
return;
}
easter_strongest();
}
if (!strcmp(cmd->command, "exit"))
{
ui_destroy_screen(that->owner);
}
}
void * ui_room_event(ui_screen_t *that, jevent e)
{
ui_room_extra_t *extra = that->data;
if (extra->time_head)
{
if (e.key.key == KEY_EXIT && e.key.type == KEYEV_UP)
{
jwidget_destroy(extra->time_head);
extra->time_head = NULL;
return extra->on_timeline ?
(void *) extra->timeline :
(void *) extra->input ;
}
return NULL;
}
if (e.type == JMLIST_ITEM_CHOSEN && !extra->file)
{
/* TODO */
int selected = jmlist_selected(extra->timeline);
jmlist_item *i = utils_array_get(extra->timeline->arr, selected);
item_extra_t *ie = i->data;
if (!ie)
{
return NULL;
}
ui_room_act(ie);
return NULL;
}
if (e.type == JMLIST_ITEM_OVERFLEW && !extra->file)
{
if (jmlist_selected(extra->timeline) != 0)
{
return NULL;
}
ui_update_history(that);
extra->timeline->widget.update = 1;
return extra->timeline;
}
if (e.type == JMLIST_ITEM_NOP && !extra->file)
{
ui_room_extra_t *extra = that->data;
m_event_t *e;
ssize_t i;
size_t from;
uint8_t code = extra->input->mode;
e = utils_array_get(extra->room->timeline, 0);
that->owner->waiting = true;
matrix_update_room_history(extra->user, extra->room, e, 10);
from = is_in_timeline(extra->room, e->event_id);
if (from != 0)
{
for (i = from - 1; i >= 0; i--)
{
m_event_t *e = utils_array_get(
extra->room->timeline,
i
);
item_extra_t *ie = malloc(sizeof(item_extra_t));
ie->event = e;
ie->screen = that;
ie->extra = that->data;
jmlist_add_item(extra->timeline, ie);
if (matrix_get_relation_type(e))
{
jmlist_item *item;
item_extra_t *itemextra;
int event = find_event_in_timeline(
extra->timeline,
matrix_get_relation_source(e)
);
if (event == -1)
{
log_text("Couldn't find!");
continue;
}
item = utils_array_get(extra->timeline->arr, event);
itemextra = item->data;
matrix_add_relation(
itemextra->event->event_id,
extra->room,
e
);
jmlist_redo(extra->timeline, event);
log_text("Have to redo(%d->%s)!", event, itemextra->event->event_id);
}
}
}
that->owner->waiting = false;
extra->input->mode = code; /* It seems like the input doesn't keep
its mode, somehow??????? */
((ui_room_extra_t *) that->data)->flag1 = false;
if (extra->jfk->labels)
{
rtc_time_t t;
rtc_get_time(&t);
char *new;
free((void *) extra->jfk->labels);
new = utils_sprintf(
"@MESG;@ROOM;/FILE;#%02d:%02d;;@EXIT",
t.hours, t.minutes
);
jfkeys_set(extra->jfk, new);
}
return NULL;
}
if (e.type == JINPUT_VALIDATED && !extra->file)
{
m_event_t *event;
const char *input = jinput_value(extra->input);
if (strlen(input) > 0 && *input == '/')
{
command_t *cmd = command_parse((char *)input);
if (cmd)
{
manage_command(that, cmd);
command_free(cmd);
return extra->timeline;
}
return extra->timeline;
}
event = send_msg(extra);
that->owner->waiting = true;
matrix_update_room_history(extra->user, extra->room, event, 5);
that->owner->waiting = false;
return extra->timeline;
}
@ -458,12 +743,14 @@ void * ui_room_event(ui_screen_t *that, jevent e)
if (e.key.key == KEY_F1 && e.key.type == KEYEV_UP && !extra->file)
{
extra->on_timeline = !extra->on_timeline;
return (!extra->on_timeline) ? extra->input : extra->timeline;
return (!extra->on_timeline) ?
(void *) extra->input :
(void *) extra->timeline ;
}
if (e.key.key == KEY_F3 && e.key.type == KEYEV_UP && !extra->file)
{
int null;
extra->file = jfileselect_create(that->widget);
extra->file = jfileselect_create(extra->main);
jwidget_set_floating(extra->file, true);
jwidget_set_background(extra->file, C_WHITE);
@ -482,26 +769,33 @@ void * ui_room_event(ui_screen_t *that, jevent e)
(void) null;
return extra->file;
}
if (e.key.key == KEY_F5 && e.key.type == KEYEV_UP && !extra->file)
if (e.key.key == KEY_F4 && e.key.type == KEYEV_UP && !extra->file)
{
char i;
extra->charpick = jmlist_create(that->widget, set_chr, NULL);
for (i = ' '; i < 0x7F; i++)
{
jmlist_add_item(extra->charpick, (void *) ((uintptr_t) i));
}
jlabel *text;
jwidget_set_floating(extra->charpick, true);
jwidget_set_background(extra->charpick, C_WHITE);
jwidget_set_maximum_size(
extra->charpick,
extra->tl_widget->w, extra->tl_widget->h
extra->time_head = jwidget_create(that->widget);
jwidget_set_floating(extra->time_head, true);
jwidget_set_fixed_size(extra->time_head, DWIDTH - 20, DHEIGHT - 90);
jwidget_set_border(extra->time_head, J_BORDER_SOLID, 1, C_BLACK);
jwidget_set_background(extra->time_head, C_WHITE);
extra->time_head->x = (DWIDTH - extra->time_head->max_w) / 2;
extra->time_head->y = (DHEIGHT - extra->time_head->max_h) / 2;
text = jlabel_create(
"Hey, sillyhead!" "\n"
"This button doesn't serve any purpose, " "\n"
"its just here to show the date!" "\n\n"
"So yeah, press EXIT to get out of there "
"kthxbye" "\n\n",
extra->time_head
);
jwidget_set_minimum_size(extra->charpick, DWIDTH, DHEIGHT - 35);
jmlist_select(extra->charpick, 1);
return extra->charpick;
jwidget_set_stretch(text, 1, 1, false);
jwidget_set_padding(text, 1, 1, 1, 1);
return extra->time_head;
}
if (e.key.key == KEY_F6 && e.key.type == KEYEV_UP && !extra->file)
if ((e.key.key == KEY_F6 || e.key.key == KEY_EXIT) &&
e.key.type == KEYEV_UP && !extra->file)
{
ui_destroy_screen(that->owner);
return NULL;

139
src/ui_roomdir.c Normal file
View File

@ -0,0 +1,139 @@
#include "matrix.h"
#include <ui_generators.h>
#include <justui/jinput.h>
#include <justui/jlabel.h>
#include <stdlib.h>
#include <utils.h>
#include <log.h>
#include <ui.h>
typedef struct chunk_extra {
m_room_chunk_t *chunk;
ui_session_t *session;
} chunk_extra_t;
static void set(jmlist_item *item, int idx)
{
jlabel *room_name, *room_topic;
jwidget *vbox;
chunk_extra_t *extra = item->data;
m_room_chunk_t *chunk = extra->chunk;
jwidget_set_stretch(item->widget, 1, 0, false);
jlayout_set_hbox(item->widget)->spacing = 1;
if (chunk->avatar_url)
{
matrix_image_from_mxc(
item->widget,
extra->session->user,
chunk->avatar_url,
32
);
}
vbox = jwidget_create(item->widget);
jwidget_set_stretch(vbox, 1, 0, false);
jlayout_set_vbox(vbox)->spacing = 1;
room_name = jlabel_create("", vbox);
jwidget_set_stretch(room_name, 1, 0, false);
jlabel_asprintf(room_name, "%s", chunk->room_id);
if (chunk->canonical_alias)
{
jlabel_asprintf(room_name, "%s", chunk->canonical_alias);
}
if (chunk->name)
{
jlabel_asprintf(room_name, "%s", chunk->name);
}
room_topic = jlabel_create("", vbox);
if (chunk->topic)
{
jlabel_asprintf(room_topic, "%s", chunk->topic);
}
(void) idx;
}
typedef struct {
jmlist *serv_list;
jinput *server_name;
} roomdir_extra_t;
void * ui_roomdir_init(ui_screen_t *that)
{
jwidget *main = jwidget_create(that->owner->stack);
jinput *server_name;
jmlist *serv_list;
roomdir_extra_t *extra = malloc(sizeof(roomdir_extra_t));
jlayout_set_vbox(main)->spacing = 1;
jwidget_set_padding(main, 0, 0, 0, 0);
jwidget_set_stretch(main, 15, 15, false);
server_name = jinput_create("Server: ", 20, main);
jwidget_set_stretch(server_name, 1, 0, false);
jwidget_set_border(server_name, J_BORDER_SOLID, 1, C_BLACK);
serv_list = jmlist_create(main, set, NULL); /* TODO */
/* TODO */
extra->server_name = server_name;
extra->serv_list = serv_list;
that->data = extra;
return server_name;
}
void * ui_roomdir_event(ui_screen_t *that, jevent e)
{
/* TODO */
roomdir_extra_t *extra = that->data;
if (e.type == JINPUT_VALIDATED)
{
char *v = (char *) jinput_value(extra->server_name);
array_t *dir = matrix_get_directory(that->owner->user, v, 20);
size_t i;
for (i = 0; i < utils_array_size(dir); i++)
{
m_room_chunk_t *chunk = utils_array_get(dir, i);
chunk_extra_t *c_extra = malloc(sizeof(*c_extra));
c_extra->chunk = chunk;
c_extra->session = that->owner;
jmlist_add_item(extra->serv_list, c_extra);
log_text("id=%s", chunk->room_id);
}
jmlist_select(extra->serv_list, 0);
utils_free_array(dir);
return extra->serv_list;
}
if (e.type == JMLIST_ITEM_CHOSEN)
{
int selected = jmlist_selected(extra->serv_list);
jmlist_item *item = utils_array_get(extra->serv_list->arr, selected);
chunk_extra_t *c_extra = item->data;
m_room_chunk_t *chunk = c_extra->chunk;
char *room_id = chunk->room_id;
m_room_t *room;
log_text("Joining id='%s'", room_id);
room = matrix_join_room(c_extra->session->user, room_id);
if (!room)
{
return NULL;
}
c_extra->session->rooms_dirty = true;
c_extra->session->new_id = room->id;
ui_destroy_screen(that->owner);
return NULL;
}
if (e.key.key == KEY_EXIT)
{
ui_destroy_screen(that->owner);
return NULL;
}
return NULL;
}

View File

@ -1,8 +1,8 @@
#include <ui.h>
#include <gint/defs/timeout.h>
#include <gint/usb-ff-bulk.h>
#include <gint/usb.h>
#include <gint/defs/timeout.h>
#include <justui/jwidget.h>
#include <justui/jlabel.h>
@ -16,6 +16,7 @@
#include <ui_generators.h>
#include <matrix.h>
#include <utils.h>
#include <log.h>
typedef struct ui_userroom {
m_user_t *user;
@ -27,31 +28,39 @@ extern bopti_image_t user;
static void set(jmlist_item *item, int idx)
{
char *id;
char *name;
char *name, *avatar;
size_t joined;
jwidget *content_box;
jlabel *room_label;
ui_userroom_t *data = item->data;
id = data->room->id;
joined = data->room->joined;
name = matrix_resolve_name(data->user, data->room);
avatar = matrix_resolve_avatar(data->user, data->room);
m_event_t *e = utils_array_get(data->room->timeline, 0);
jwidget_set_stretch(item->widget, 1, 0, false);
jlayout_set_vbox(item->widget)->spacing = 1;
jlayout_set_hbox(item->widget)->spacing = 1;
room_label = jlabel_create("", item->widget);
matrix_image_from_mxc(item->widget, data->user, avatar, 32);
content_box = jwidget_create(item->widget);
jwidget_set_stretch(content_box, 1, 0, false);
jlayout_set_vbox(content_box)->spacing = 1;
room_label = jlabel_create("", content_box);
jlabel_asprintf(room_label, "%s", name ? name : id);
free(name);
jwidget_set_stretch(room_label, 1, 0, false);
if (e && !strcmp(e->type, "m.room.message"))
{
jlabel *message = jlabel_create("", item->widget);
jwidget *hbox = jwidget_create(item->widget);
jlabel *message = jlabel_create("", content_box);
jwidget *hbox = jwidget_create(content_box);
jwidget *hpad = jwidget_create(hbox);
jlabel *user_count;
jpainted *user_painted;
jwidget *ulist = jwidget_create(hbox);
char *body = utils_json_as_string(
utils_hashmap_get(e->content, "body")
@ -67,7 +76,7 @@ static void set(jmlist_item *item, int idx)
jlayout_set_hbox(hbox)->spacing = 2;
user_count = jlabel_create("", ulist);
user_painted = ui_image(ulist, &user);
ui_image(ulist, &user);
jlayout_set_hbox(ulist)->spacing = 5;
jlabel_asprintf(user_count, "%lu", joined);
@ -80,7 +89,7 @@ static void set(jmlist_item *item, int idx)
jwidget_set_stretch(message, 1, 0, false);
jwidget_set_maximum_width(message, DWIDTH - 10);
}
(void) idx;
}
void * ui_rooms_init(ui_screen_t *that)
@ -95,7 +104,7 @@ void * ui_rooms_init(ui_screen_t *that)
char *key;
void *data;
size_t i;
that->owner->rooms_dirty = false;
wid = jwidget_create(that->owner->stack);
jlayout_set_vbox(wid)->spacing = 1;
@ -123,7 +132,7 @@ void * ui_rooms_init(ui_screen_t *that)
jwidget_set_padding(rooms_list, 1, 1, 1, 1);
jwidget_set_stretch(rooms_list, 15, 15, false);
jfk = jfkeys_create("/INFOS;@DIR;;;#ACC;#JOIN", wid);
jfk = jfkeys_create("/INFOS;@DIRECTORY;;;;#ACCOUNT", wid);
jwidget_set_stretch(jfk, 1, 0, false);
that->data = rooms_list;
@ -155,26 +164,36 @@ void * ui_rooms_event(ui_screen_t *that, jevent e)
infos = ui_new_screen(ui_infos_init, NULL, ui_infos_event);
ui_add_screen(that->owner, infos);
return NULL;
}
if (e.key.key == KEY_F2)
{
ui_screen_t *dir;
dir = ui_new_screen(ui_roomdir_init, NULL, ui_roomdir_event);
ui_add_screen(that->owner, dir);
return NULL;
}
if (e.key.key == KEY_F6 && e.key.type == KEYEV_UP)
{
ui_screen_t *accinfo;
}
if (e.key.key == KEY_F2 && e.key.type == KEY_UP)
{
/* TODO: Room directory screen */
return NULL;
}
if (e.key.key == KEY_F5 && e.key.type == KEY_UP)
{
/* TODO: Account information screen */
return NULL;
}
if (e.key.key == KEY_F6 && e.key.type == KEY_UP)
{
/* TODO: Room join screen */
accinfo = ui_new_screen(ui_accinfo_init, ui_accinfo_focus, ui_accinfo_event);
ui_add_screen(that->owner, accinfo);
return NULL;
}
return NULL;
}
void * ui_rooms_focus(ui_screen_t *that)
{
hashmap_t *rooms = that->owner->user->rooms;
if (that->owner->rooms_dirty)
{
m_room_t *room = utils_hashmap_get(rooms, that->owner->new_id);
ui_userroom_t *pack = malloc(sizeof(ui_userroom_t));
pack->room = room;
pack->user = that->owner->user;
jmlist_add_item(that->data, pack);
that->owner->rooms_dirty = false;
}
return that->data;
}

View File

@ -17,6 +17,7 @@
void usb_init(int *out, int *in)
{
timeout_t tm;
static bool first_time = true;
usb_interface_t const *intf[] = { &usb_ff_bulk, NULL };
usb_open(intf, GINT_CALL_NULL);
@ -25,11 +26,15 @@ void usb_init(int *out, int *in)
while (!usb_is_open() && !timeout_elapsed(&tm))
{
timeout_t tm1 = timeout_make_ms(66);
easter_dvd_frame(
"Uplinking with USB (run mastrix on your PC now)..."
);
while (!timeout_elapsed(&tm1));
if (first_time)
{
easter_dvd_frame(
"Uplinking with USB (run mastrix on your PC now)..."
);
while (!timeout_elapsed(&tm1));
}
}
first_time = false;
if (!usb_is_open())
{
@ -59,7 +64,7 @@ int usb_except_message(const char *app, const char *type, usb_except_cb cb)
{
usb_fxlink_header_t header;
timeout_t tm;
static volatile uint64_t idles = 0;
static volatile int64_t idles = 0;
static volatile int64_t idlemax = -1;
int rc;
@ -89,7 +94,7 @@ int usb_except_message(const char *app, const char *type, usb_except_cb cb)
idles++;
return idlemax == -1 ? 0 : (idles > idlemax ? -2 : 0);
}
if (rc < sizeof(usb_fxlink_header_t))
if ((unsigned int) rc < sizeof(usb_fxlink_header_t))
{
idles++;
return idlemax == -1 ? 0 : (idles > idlemax ? -2 : 0);

1060
src/yxml.c Normal file

File diff suppressed because it is too large Load Diff