diff options
-rw-r--r-- | README.rst | 6 | ||||
-rw-r--r-- | host/.gitignore | 5 | ||||
-rw-r--r-- | host/Makefile | 12 | ||||
-rwxr-xr-x | host/aus.py | 20 | ||||
-rw-r--r-- | host/bdf.c (renamed from host/main.c) | 91 | ||||
-rw-r--r-- | host/bdf.h | 11 | ||||
-rw-r--r-- | host/color.h | 9 | ||||
-rw-r--r-- | host/default.lines | 259 | ||||
-rw-r--r-- | host/font.c | 13 | ||||
-rw-r--r-- | host/font.h | 3 | ||||
-rw-r--r-- | host/main.h | 10 | ||||
-rw-r--r-- | host/matelight.py | 40 | ||||
-rw-r--r-- | host/matelight.service | 10 | ||||
-rw-r--r-- | host/net.h | 18 | ||||
-rwxr-xr-x | host/server.py | 239 | ||||
-rw-r--r-- | host/terminal.py | 19 | ||||
-rw-r--r-- | host/usb.c | 161 | ||||
-rw-r--r-- | host/usb.h | 27 |
18 files changed, 740 insertions, 213 deletions
@@ -31,13 +31,19 @@ Related Projects ---------------- * `A Python script that plays gifs over CRAP`_ * `An HTML5 CRAP emulator`_ +* `A CRAP client for node.js`_ * `Webcam streaming on Mate Light`_ * `A game programming framework for Mate Light`_ * `Snake for Mate Light`_ * `Game of Life for Mate Light`_ +* `Mate Light Android App`_ +* `Blinkenlights for Mate Light`_ +* `Postillon Newsticker for Matelight`_ .. _`A Python script that plays gifs over CRAP`: https://github.com/uwekamper/matelight-gifplayer .. _`An HTML5 CRAP emulator`: https://github.com/sodoku/matelightemu +.. _`A CRAP client for node.js`: https://github.com/sodoku/node-matelight +.. _`Postillon Newsticker for Matelight`: https://gist.github.com/XenGi/9168633 .. _`Webcam streaming on Mate Light`: https://github.com/c-base/matetv .. _`A game programming framework for Mate Light`: https://github.com/c-base/pymlgame .. _`Snake for Mate Light`: https://github.com/c-base/pymlsnake diff --git a/host/.gitignore b/host/.gitignore new file mode 100644 index 0000000..1903c5d --- /dev/null +++ b/host/.gitignore @@ -0,0 +1,5 @@ +__pycache__ +env +*.so +*.swo +*.swp diff --git a/host/Makefile b/host/Makefile index a6b892f..85536f5 100644 --- a/host/Makefile +++ b/host/Makefile @@ -1,6 +1,12 @@ -all: main.c font.c font.h color.c color.h - gcc -shared -fPIC -std=gnu11 -Wall -lm -o libbdf.so -g -O0 main.c font.c color.c +all: libml libbdf + + +libml: usb.c color.c color.h + gcc -shared -fPIC -std=gnu11 -Wall -lm -lusb-1.0 -o libml.so -g usb.c color.c + +libbdf: bdf.c font.c font.h color.c color.h + gcc -shared -fPIC -std=gnu11 -Wall -lm -o libbdf.so -g bdf.c font.c color.c clean: - rm libbdf.so + rm libbdf.so libml.so diff --git a/host/aus.py b/host/aus.py new file mode 100755 index 0000000..c0bab71 --- /dev/null +++ b/host/aus.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python + +from socketserver import * +import socket +import struct +import zlib +from time import time, strftime, sleep +from collections import namedtuple, deque +import itertools +import threading +import random +import os + +from ctypes import * + +from matelight import sendframe, DISPLAY_WIDTH, DISPLAY_HEIGHT, FRAME_SIZE + +if __name__ == '__main__': + sendframe(bytes([0, 0, 0]*FRAME_SIZE)) + @@ -1,9 +1,8 @@ #include "config.h" -#include "main.h" +#include "bdf.h" #include "color.h" #include "font.h" -#include "net.h" #include <stdint.h> #include <stdlib.h> #include <stdio.h> @@ -18,20 +17,11 @@ #include <netinet/in.h> #include <unistd.h> - -void free_framebuffer(framebuffer_t *fb){ - free(fb->data); - free(fb); -} - /* CAUTION: REQUIRES INPUT TO BE \0-TERMINATED * ...also, it does a hardcoded setlocale of LC_CTYPE to en_US.utf8 for... reasons. */ -framebuffer_t *framebuffer_render_text(char *s, glyphtable_t *glyph_table){ - unsigned int len = strlen(s); - - color_t *gbuf = NULL; - unsigned int gbufwidth = 0; - unsigned int gbufheight = 0; +int framebuffer_get_text_bounds(char *s, glyphtable_t *glyph_table, size_t *outw, size_t *outh){ + size_t gbufwidth = 0; + size_t gbufheight = 0; char *p = s; /* Calculate screen width of string prior to allocating memory for the frame buffer */ @@ -65,7 +55,7 @@ framebuffer_t *framebuffer_render_text(char *s, glyphtable_t *glyph_table){ p += inc; if(c > glyph_table->size){ - fprintf(stderr, "Error rendering string: Codepoint 0x%lx out of valid range (0-%ld).\n", (long int)c, glyph_table->size); + fprintf(stderr, "Error rendering string: Codepoint 0x%lx out of valid range (0-%zd).\n", (long int)c, glyph_table->size); goto error; } @@ -81,19 +71,31 @@ framebuffer_t *framebuffer_render_text(char *s, glyphtable_t *glyph_table){ gbufwidth += g->width; } - /* For easier rendering on the terminal, round up to multiples of two */ - gbufheight += gbufheight&1; + *outw = gbufwidth; + *outh = gbufheight; + return 0; +error: + return 1; +} + +/* CAUTION: REQUIRES INPUT TO BE \0-TERMINATED + * ...also, it does a hardcoded setlocale of LC_CTYPE to en_US.utf8 for... reasons. */ +/* Render the string beginning from the specified x offset (in pixels) */ +int framebuffer_render_text(char *s, glyphtable_t *glyph_table, color_t *gbuf, size_t gbufwidth, size_t gbufheight, size_t offx){ + unsigned int len = strlen(s); + char *p = s; - size_t gbufsize = gbufwidth*gbufheight; - gbuf = calloc(gbufsize, sizeof(color_t)); - if(!gbuf){ - fprintf(stderr, "Cannot malloc() %lu bytes.\n", gbufsize*sizeof(color_t)); + if(!setlocale(LC_CTYPE, "en_US.utf8")){ + fprintf(stderr, "Cannot set locale\n"); goto error; } - memset(gbuf, 0, gbufsize*sizeof(color_t)); + + memset(gbuf, 0, gbufwidth*gbufheight*sizeof(color_t)); unsigned int x = 0; + wchar_t c; p = s; + mbstate_t ps = {0}; memset(&ps, 0, sizeof(mbstate_t)); struct { color_t fg; @@ -280,38 +282,35 @@ framebuffer_t *framebuffer_render_text(char *s, glyphtable_t *glyph_table){ color_t bg = inv ? style.bg : style.fg; glyph_t *g = glyph_table->data[c]; - render_glyph(g, gbuf, gbufwidth, x, 0, fg, bg); - if(style.strikethrough || style.underline){ - int sty = gbufheight/2; - /* g->y usually is a negative index of the glyph's baseline measured from the glyph's bottom */ - int uly = gbufheight + g->y; - for(int i=0; i<g->width; i++){ - if(style.strikethrough) - gbuf[sty*gbufwidth + x + i] = fg; - if(style.underline) - gbuf[uly*gbufwidth + x + i] = fg; + /* Is the glyph within the buffer's bounds? */ + //if(x+g->width > offx && x < offx+gbufwidth){ + /* x-offx might be negative down to -g->width+1, but that's ok */ + render_glyph(g, gbuf, gbufwidth, x-offx, 0, fg, bg); + if(style.strikethrough || style.underline){ + int sty = gbufheight/2; + /* g->y usually is a negative index of the glyph's baseline measured from the glyph's bottom */ + int uly = gbufheight + g->y; + for(int i=0; i<g->width; i++){ + if(x+i >= offx && x+i-offx < gbufwidth){ /* Stay within the frame buffer's bounds */ + if(style.strikethrough) + gbuf[sty*gbufwidth + x + i - offx] = fg; + if(style.underline) + gbuf[uly*gbufwidth + x + i - offx] = fg; + } + } } - } + //} x += g->width; } - framebuffer_t *fb = malloc(sizeof(framebuffer_t)); - if(!fb){ - fprintf(stderr, "Cannot malloc() %lu bytes.\n", sizeof(framebuffer_t)); - goto error; - } - fb->w = gbufwidth; - fb->h = gbufheight; - fb->data = gbuf; - return fb; -error: - free(gbuf); return 0; +error: + return 1; } void console_render_buffer(color_t *data, size_t w, size_t h){ /* Render framebuffer to terminal, two pixels per character using Unicode box drawing stuff */ color_t lastfg = {0, 0, 0}, lastbg = {0, 0, 0}; - printf("\e[38;5;0;48;5;0m"); + printf("\e[38;5;0;48;5;0m\e[K"); for(size_t y=0; y < h; y+=2){ for(size_t x=0; x < w; x++){ /* Da magicks: ▀█▄ */ @@ -350,7 +349,7 @@ void console_render_buffer(color_t *data, size_t w, size_t h){ } } } - printf("\n"); + printf("\n\e[K"); } printf("\033[0m"); } diff --git a/host/bdf.h b/host/bdf.h new file mode 100644 index 0000000..c81277e --- /dev/null +++ b/host/bdf.h @@ -0,0 +1,11 @@ +#ifndef __MAIN_H__ +#define __MAIN_H__ + +#include "color.h" +#include "font.h" + +int framebuffer_get_text_bounds(char *s, glyphtable_t *glyph_table, size_t *outw, size_t *outh); +int framebuffer_render_text(char *s, glyphtable_t *glyph_table, color_t *gbuf, size_t gbufwidth, size_t gbufheight, size_t offx); +void console_render_buffer(color_t *data, size_t w, size_t h); + +#endif//__MAIN_H__ diff --git a/host/color.h b/host/color.h index 1fbfe57..2a9d345 100644 --- a/host/color.h +++ b/host/color.h @@ -6,13 +6,14 @@ /* For easier memsetting we use an inverted alpha channel, i.e. 0 ≘ fully opaque; 255 ≘ fully transparent */ typedef struct { - uint8_t r; - uint8_t g; - uint8_t b; - uint8_t a; + uint8_t r, g, b, a; } color_t; typedef struct { + uint8_t r, g, b; +} rgb_t; + +typedef struct { color_t *data; size_t w; size_t h; diff --git a/host/default.lines b/host/default.lines new file mode 100644 index 0000000..ecf0862 --- /dev/null +++ b/host/default.lines @@ -0,0 +1,259 @@ +\x1B[38;5;214mabstraction is a type of decadence +\x1B[38;5;214mit's important to stay clean on all levels +\x1B[38;5;214mexpiring for love is beautiful but stupid +\x1B[38;5;214mlack of charisma can be fatal +\x1B[38;5;214mambition is just as dangerous as complacency +\x1B[38;5;214mslipping into madness is good for the sake of comparison +\x1B[38;5;214mplanning for the future is escapism +\x1B[38;5;214mtrue freedom is frightful +\x1B[92mMate Light\x1B[0m powered by \x1B[95mData Becker™ \x1B[96mLaufschriftstudio 2000® Platinum Edition +\x1B[38;5;214mreligion causes as many problems as it solves +\x1B[38;5;214mdisorganization is a kind of anesthesia +\x1B[38;5;214mpursuing pleasure for the sake of pleasure will ruin you +\x1B[38;5;214manger or hate can be a useful motivating force +\x1B[38;5;214mjust believing something can make it happen +\x1B[38;5;214mtrading a life for a life is fair enough +\x1B[38;5;214mlisten when your body talks +\x1B[38;5;214mfake or real indifference is a powerful personal weapon +\x1B[38;5;214mcalm is more conductive to creativity than is anxiety +\x1B[38;5;214mthere are too few immutable truths today +\x1B[38;5;214mbeing happy is more important than anything else +\x1B[38;5;214mit can be helpful to keep going no matter what +\x1B[38;5;214mbelieving in rebirth is the same as admitting defeat +\x1B[38;5;214mit's vital to live in harmony with nature +\x1B[38;5;214mdisgust is the appropriate response to most situations +\x1B[38;5;214mnothing upsets the balance of good and evil +\x1B[38;5;214mrandom mating is good for debunking sex myths +\x1B[38;5;214manimalism is perfectly healthy +\x1B[92mMate Light\x1B[0m powered by \x1B[95mSiemens™ \x1B[96mLaufschrift® v.0.1.2b fuer Intel™ Pentium® +\x1B[38;5;214msalvation can't be bought and sold +\x1B[38;5;214mextreme self-consciousness leads to perversion +\x1B[38;5;214mroutine small excesses are worse than then the occasional debauch +\x1B[38;5;214mwe must make sacrifices to maintain our quality of life +\x1B[38;5;214mmuch was decided before you were born +\x1B[38;5;214ma positive attitude means all the difference in the world +\x1B[38;5;214myou must have one grand passion +\x1B[38;5;214mit is heroic to try to stop time +\x1B[38;5;214moccasionally principles are more valuable than people +\x1B[38;5;214ma sense of timing is the mark of genius +\x1B[38;5;214mexpressing anger is necessary +\x1B[38;5;214mroutine is a link with the past +\x1B[38;5;214mtorture is barbaric +\x1B[38;5;214melaboration is a form of pollution +\x1B[38;5;214mpeople are responsible for what they do unless they are insane +\x1B[38;5;214mromantic love was invented to manipulate women +\x1B[38;5;214msex differences are here to stay +\x1B[38;5;214mthe unattainable is invariable attractive +\x1B[38;5;214ma man can't know what it is to be a mother +\x1B[38;5;214mthere's nothing except what you sense +\x1B[38;5;214msurvival of the fittest applies to men and animals +\x1B[38;5;214man elite is inevitable +\x1B[38;5;214mit is man's fate to outsmart himself +\x1B[38;5;214mpeople won't behave if they have nothing to lose +\x1B[38;5;214mpeople are nuts if they think they are important +\x1B[38;5;214many surplus is immoral +\x1B[38;5;214mremember you always have freedom of choice +\x1B[38;5;214ma lot of professionals are crackpots +\x1B[38;5;214msacrificing yourself for a bad cause is not a moral act +\x1B[38;5;214mit's better to be naive than jaded +\x1B[38;5;214mdependence can be a meal ticket +\x1B[38;5;214mkeep something in reserve for emergencies +\x1B[38;5;214mold friends are better left in the past +\x1B[38;5;214mmoney creates taste +\x1B[38;5;214mto disagree presupposes moral integrity +\x1B[38;5;214mbeing sure of yourself means you're a fool +\x1B[38;5;214myou must know where you stop and the world begins +\x1B[38;5;214mideals are replaced by conventional goals at a certain age +\x1B[38;5;214mmost people are not fit to rule themselves +\x1B[38;5;214mguilt and self-laceration are indulgences +\x1B[38;5;214mit's better to be lonely than to be with inferior people +\x1B[38;5;214mdying and coming back gives you considerable perspective +\x1B[38;5;214mabsolute submission can be a form of freedom +\x1B[38;5;214mfear is the greatest incapacitator +\x1B[38;5;214mholding back protects your vital energies +\x1B[38;5;214mwhen something terrible happens people wake up +\x1B[38;5;214mensure that your life stays in flux +\x1B[38;5;214mbeing judgmental is a sign of life +\x1B[38;5;214mfreedom is a luxury not a necessity +\x1B[38;5;214mdrama often obscures the real issues +\x1B[38;5;214mwith perseverance you can discover any truth +\x1B[38;5;214mhumanism is obsolete +\x1B[38;5;214myou must disagree with authority figures +\x1B[38;5;214mmyth can make reality more intelligible +\x1B[38;5;214mhabitual contempt doesn't reflect a finer sensibility +\x1B[38;5;214msterilization is a weapon of the rulers +\x1B[38;5;214myour oldest fears are the worst ones +\x1B[38;5;214moffer very little information about yourself +\x1B[38;5;214mthreatening someone sexually is a horrible act +\x1B[38;5;214mbeing alone with yourself is increasingly unpopular +\x1B[38;5;214myou are a victim of the rules you live by +\x1B[38;5;214mto volunteer is reactionary +\x1B[38;5;214myou have to hurt others to be extraordinary +\x1B[38;5;214mabuse of power comes as no surprise +\x1B[38;5;214mrecluses always get weak +\x1B[38;5;214mawful punishment awaits really bad people +\x1B[38;5;214mchildren are the most cruel of all +\x1B[38;5;214mthe cruelest disappointment is when you let yourself down +\x1B[38;5;214mgo all out in romance and let the chips fall where they may +\x1B[38;5;214mthe only way to be pure is to stay by yourself +\x1B[38;5;214mspending too much time on self-improvement is antisocial +\x1B[38;5;214mknowing yourself lets you understand others +\x1B[38;5;214mresolutions serve to ease our conscience +\x1B[38;5;214mwar is a purification rite +\x1B[38;5;214mgiving free rein to your emotions is an honest way to live +\x1B[38;5;214mpeople who go crazy are too sensitive +\x1B[38;5;214mit's better to study the living fact than to analyze history +\x1B[38;5;214mmen are not monogamous by nature +\x1B[38;5;214mclass action is a nice idea with no substance +\x1B[38;5;214mdreaming while awake is a frightening contradiction +\x1B[38;5;214millness is a state of mind +\x1B[38;5;214myou are responsible for constituting the meaning of things +\x1B[38;5;214mit's good to give extra money to charity +\x1B[38;5;214mit's better to be a good person than a famous person +\x1B[38;5;214moften you should act like you are sexless +\x1B[38;5;214mpeople who don't work with their hands are parasites +\x1B[38;5;214mmonomania is a prerequisite of success +\x1B[38;5;214mchasing the new is dangerous to society +\x1B[38;5;214mrevolution begins with changes in the individual +\x1B[38;5;214mselflessness is the highest achievement +\x1B[38;5;214msymbols are more meaningful than things themselves +\x1B[38;5;214ma strong sense of duty imprisons you +\x1B[38;5;214mautomation is deadly +\x1B[38;5;214mopacity is an irresistible challenge +\x1B[38;5;214mmurder has its sexual side +\x1B[38;5;214mat times inactivity is preferable to mindless functioning +\x1B[38;5;214mmostly you should mind your own business +\x1B[38;5;214mthe mundane is to be cherished +\x1B[38;5;214mselfishness is the most basic motivation +\x1B[38;5;214msloppy thinking gets worse over time +\x1B[38;5;214myou can't expect people to be something they're not +\x1B[38;5;214mambivalence can ruin your life +\x1B[38;5;214munquestioning love demonstrates largesse of spirit +\x1B[38;5;214mlabor is a life-destroying activity +\x1B[38;5;214mgovernment is a burden on the people +\x1B[38;5;214mdecency is a relative thing +\x1B[38;5;214myour actions ae pointless if no one notices +\x1B[38;5;214mdying should be as easy as falling off a log +\x1B[38;5;214martificial desires are despoiling the earth +\x1B[38;5;214mcategorizing fear is calming +\x1B[38;5;214mit's just an accident that your parents are your parents +\x1B[38;5;214meven your family can betray you +\x1B[38;5;214mbad intentions can yield good results +\x1B[38;5;214mif you have many desires your life will be interesting +\x1B[92mMate Light\x1B[93m@\x1B[92mPlay store or \x1B[94;101mtcp://ml.jaseg.net:1337\x1B[0;91m ♥ +\x1B[38;5;214mit's not good to operate on credit +\x1B[38;5;214mdescription is more important than metaphor +\x1B[38;5;214mhiding your emotions is despicable +\x1B[38;5;214mthe desire to reproduce is a death wish +\x1B[38;5;214mpush yourself to the limit as often as possible +\x1B[38;5;214mextreme behavior has its basis in pathological psychology +\x1B[38;5;214mthere's nothing redeeming in toil +\x1B[38;5;214myou are guileless in your dreams +\x1B[38;5;214manything is a legitimate area of investigation +\x1B[38;5;214mdeviants are sacrificed to increase group solidarity +\x1B[38;5;214myou can't fool others if you're fooling yourself +\x1B[38;5;214myou can understand someone of your sex only +\x1B[38;5;214mraise boys and girls the same way +\x1B[38;5;214mit's not good to hold too many absolutes +\x1B[38;5;214mkilling is unavoidable but nothing to be proud of +\x1B[38;5;214myou should study as much as possible +\x1B[38;5;214malienation produces eccentrics or revolutionaries +\x1B[38;5;214mtimidity is laughable +\x1B[38;5;214mfathers often use too much force +\x1B[38;5;214mgrass roots agitation is the only hope +\x1B[38;5;214mstrong emotional attachment stems from basic insecurity +\x1B[38;5;214mrechanneling destructive impulses is a sign of maturity +\x1B[92mMate Light\x1B[0m powered by \x1B[95mApple™ \x1B[96miScroll®' +\x1B[38;5;214msometimes science advances faster than it should +\x1B[38;5;214ma sincere effort is all you can ask +\x1B[38;5;214myou owe the world not the other way around +\x1B[38;5;214mmothers shouldn't make too many sacrifices +\x1B[38;5;214mloving animals is a substitute activity +\x1B[38;5;214mchange is valuable when the oppressed become tyrants +\x1B[38;5;214msolitude is enriching +\x1B[38;5;214msometimes things seem to happen of their own accord +\x1B[38;5;214mviolence is permissible even desirable occasionally +\x1B[38;5;214mall things are delicately interconnected +\x1B[38;5;214mplaying it safe can cause a lot of damage in the long run +\x1B[38;5;214mdecadence can be an end in itself +\x1B[92mMate Light\x1B[0;91m ♥ \x1B[92mUnicode +\x1B[38;5;214meveryone's work is equally important +\x1B[38;5;214mtaking a strong stand publicizes the opposite position +\x1B[38;5;214mpain can be a very positive thing +\x1B[38;5;214mclass structure is as artificial as plastic +\x1B[38;5;214mthe world operates according to discoverable laws +\x1B[38;5;214mteasing people sexually can have ugly consequences +\x1B[38;5;214mredistributing wealth is imperative +\x1B[38;5;214maction causes more trouble than thought +\x1B[38;5;214mignoring enemies is the best way to fight +\x1B[38;5;214mnoise can be hostile +\x1B[38;5;214mwishing things away is not effective +\x1B[38;5;214mit is a gift to the world not to have babies +\x1B[38;5;214mthe sum of your actions determines what you are +\x1B[38;5;214ma name means a lot just by itself +\x1B[38;5;214mthinking too much can only cause problems +\x1B[38;5;214msin is a means of social control +\x1B[38;5;214mchildren are the hope of the future +\x1B[38;5;214min some instances it's better to die than to continue +\x1B[38;5;214mself-awareness can be crippling +\x1B[38;5;214mprivate property created crime +\x1B[38;5;214mlow expectations are good protection +\x1B[38;5;214meverything that's interesting is new +\x1B[38;5;214mdon't place to much trust in experts +\x1B[38;5;214mstupid people shouldn't breed +\x1B[38;5;214ma relaxed man is not necessarily a better man +\x1B[38;5;214mleisure time is a gigantic smoke screen +\x1B[38;5;214mexceptional people deserve special concessions +\x1B[38;5;214mstasis is a dream state +\x1B[38;5;214mgood deeds eventually are rewarded +\x1B[38;5;214menjoy yourself because you can't change anything anyway +\x1B[38;5;214mthe new is nothing but a restatement of the old +\x1B[38;5;214mit's crucial to have an active fantasy life +\x1B[38;5;214mworrying can help you prepare +\x1B[38;5;214minheritance must be abolished +\x1B[38;5;214mpeople are boring unless they are extremists +\x1B[38;5;214mconfusing yourself is a way to stay honest +\x1B[38;5;214mstarvation is nature's way +\x1B[38;5;214mhumor is a release +\x1B[38;5;214mcrime against property is relatively unimportant +\x1B[38;5;214mat times your unconsciousness is truer than your conscious mind +\x1B[38;5;214mself-contempt can do more harm than good +\x1B[38;5;214mtalking is used to hide one's inability to act +\x1B[38;5;214mthe idea of revolution is an adolescent fantasy +\x1B[38;5;214mif you live simply there is nothing to worry about +\x1B[38;5;214ma single event can have infinitely many interpretations +\x1B[38;5;214mgoing with the flow is soothing but risky +\x1B[38;5;214mmanual labor can be refreshing and wholesome +\x1B[38;5;214mpotential counts for nothing until it's realized +\x1B[38;5;214myou are the past present and future +\x1B[38;5;214mtechnology will make or break us +\x1B[38;5;214mevery achievement requires a sacrifice +\x1B[38;5;214mthe family is living on borrowed time +\x1B[38;5;214mphysical culture is second best +\x1B[38;5;214munique things must be the most valuable +\x1B[38;5;214ma solid home base builds a sense of self +\x1B[38;5;214mthe idiosyncratic has lost its authority +\x1B[38;5;214mlooking back is the first sign of aging and decay +\x1B[38;5;214mrepetition is the best way to learn +\x1B[38;5;214mknowledge should be advanced at all costs +\x1B[38;5;214mif you aren't political your personal life should be exemplary +\x1B[38;5;214mboredom makes you do crazy things +\x1B[38;5;214myou must be intimate with a token few +\x1B[38;5;214myou can live on through your descendants +\x1B[38;5;214mseparatism is the way to a new beginning +\x1B[38;5;214mthe idea of transcendence is used to obscure oppression +\x1B[38;5;214mpolitics is used for personal gain +\x1B[38;5;214meating too much is criminal +\x1B[38;5;214mimposing order is man's vocation for chaos is hell +\x1B[38;5;214myou don't know what's what until you support yourself +\x1B[38;5;214mif you can't leave your mark give up +\x1B[38;5;214ma little knowledge can go a long way +\x1B[38;5;214musing force to stop force is absurd +\x1B[38;5;214mmorals are for little people +\x1B[38;5;214mwords tend to be inadequate +\x1B[38;5;214memotional responses ar as valuable as intellectual responses +\x1B[92mMate Light\x1B[0m powered by \x1B[95mMicrosoft™ \x1B[96mMarquee Manager® Professional OEM +\x1B[38;5;214mthe most profound things are inexpressible +\x1B[38;5;214mfaithfulness is a social not a biological law +\x1B[38;5;214mrelativity is no boon to mankind +\x1B[38;5;214mmoderation kills the spirit diff --git a/host/font.c b/host/font.c index ee9adb3..59bc2ef 100644 --- a/host/font.c +++ b/host/font.c @@ -6,7 +6,8 @@ #include <string.h> #include <errno.h> -void render_glyph(glyph_t *g, color_t *buf, unsigned int bufwidth, unsigned int offx, unsigned int offy, color_t fg, color_t bg){ +/* Clips at buffer bounds */ +void render_glyph(glyph_t *g, color_t *buf, unsigned int bufwidth, int offx, unsigned int offy, color_t fg, color_t bg){ unsigned int bitmap_row_width = g->width/8; uint8_t *bitmap = ((uint8_t *)g) + sizeof(glyph_t); for(unsigned int y=0; y < g->height; y++){ @@ -16,9 +17,13 @@ void render_glyph(glyph_t *g, color_t *buf, unsigned int bufwidth, unsigned int data |= bitmap[y*bitmap_row_width+i]; } color_t *p = buf + (offy+y)*bufwidth + offx; - for(unsigned int x=0; x < g->width; x++){ - color_t c = (data&(1<<(g->width-1))) ? fg : bg; - *p++ = c; + /* Take care to only render what's within the framebuffer's bounds */ + for(int x=0; (x < g->width) && (offx+x < (int)bufwidth); x++){ + if(offx + x >= 0){ + color_t c = (data&(1<<(g->width-1))) ? fg : bg; + *p = c; + } + p++; data <<= 1; } } diff --git a/host/font.h b/host/font.h index e9fbb35..6a6f302 100644 --- a/host/font.h +++ b/host/font.h @@ -29,7 +29,6 @@ glyphtable_t *read_bdf(FILE *f); void free_glyphtable(glyphtable_t *glyph_table); -// Requires buf to point to a buffer at least of size glyph->width*glyph->height. -void render_glyph(glyph_t *glyph, color_t *buf, unsigned int bufwidth, unsigned int offx, unsigned int offy, color_t fg, color_t bg); +void render_glyph(glyph_t *g, color_t *buf, unsigned int bufwidth, int offx, unsigned int offy, color_t fg, color_t bg); #endif//__FONT_H__ diff --git a/host/main.h b/host/main.h deleted file mode 100644 index 5ace45e..0000000 --- a/host/main.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef __MAIN_H__ -#define __MAIN_H__ - -#include "color.h" -#include "font.h" - -framebuffer_t *framebuffer_render_text(char *s, glyphtable_t *glyph_table); -void console_render_buffer(color_t *data, size_t w, size_t h); - -#endif//__MAIN_H__ diff --git a/host/matelight.py b/host/matelight.py index 7c4f6c4..1bafe66 100644 --- a/host/matelight.py +++ b/host/matelight.py @@ -1,7 +1,8 @@ -import usb import colorsys -import numpy as np from itertools import product +from ctypes import c_size_t, c_uint8, c_void_p, c_float, CDLL, Structure, POINTER +import numpy as np +import time CRATE_WIDTH = 5 CRATE_HEIGHT = 4 @@ -10,25 +11,34 @@ CRATES_Y = 4 DISPLAY_WIDTH = CRATES_X*CRATE_WIDTH DISPLAY_HEIGHT = CRATES_Y*CRATE_HEIGHT -FRAME_SIZE = CRATE_WIDTH*CRATE_HEIGHT*3 +CRATE_SIZE = CRATE_WIDTH*CRATE_HEIGHT*3 +FRAME_SIZE = DISPLAY_WIDTH*DISPLAY_HEIGHT + +# Gamma factor +GAMMA = 2.5 + +# Brightness of the LEDs in percent. 1.0 means 100%. +BRIGHTNESS = 1.0 + +ml = CDLL('./libml.so') +ml.matelight_open.restype = c_void_p -dev = usb.core.find(idVendor=0x1cbe, idProduct=0x0003) +if ml.matelight_usb_init(): + raise OSError('Cannot initialize USB library') +matelights = ml.matelight_open() +if matelights is None: + raise ImportError('Cannot open any Mate Light devices') +dbuf = np.zeros(DISPLAY_WIDTH*DISPLAY_HEIGHT*4, dtype=np.uint8) def sendframe(framedata): """ Send a frame to the display The argument contains a h * w array of 3-tuples of (r, g, b)-data or 4-tuples of (r, g, b, a)-data where the a channel is ignored. """ - # Gamma correction - framedata = (((framedata/255) ** 2.5) * 255).astype(np.uint8) - for cx, cy in product(range(CRATES_X), range(CRATES_Y)): - datar = framedata[cy*CRATE_HEIGHT:(cy+1)*CRATE_HEIGHT, cx*CRATE_WIDTH:(cx+1)*CRATE_WIDTH, :3] - data = datar.flat - if len(data) != FRAME_SIZE: - raise ValueError('Invalid frame data. Expected {} bytes, got {}.'.format(FRAME_SIZE, len(data))) - # Send framebuffer data - dev.write(0x01, bytes([0, cx, cy])+bytes(data)) - # Send latch command - dev.write(0x01, b'\x01') + # just use the first Mate Light available + rgba = len(framedata) == DISPLAY_WIDTH*DISPLAY_HEIGHT*4 + global dbuf + np.copyto(dbuf[:640*(3+rgba)], np.frombuffer(framedata, dtype=np.uint8)) + ml.matelight_send_frame(matelights, dbuf.ctypes.data_as(POINTER(c_uint8)), c_size_t(CRATES_X), c_size_t(CRATES_Y), c_float(BRIGHTNESS), rgba) diff --git a/host/matelight.service b/host/matelight.service new file mode 100644 index 0000000..7592791 --- /dev/null +++ b/host/matelight.service @@ -0,0 +1,10 @@ +[Unit] +Description=MateLight Server + +[Service] +WorkingDirectory=/root/matelight/host +ExecStart=/root/matelight/host/server.py +Type=simple + +[Install] +WantedBy=multi-user.target diff --git a/host/net.h b/host/net.h deleted file mode 100644 index d8ec949..0000000 --- a/host/net.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef __NET_H__ -#define __NET_H__ - -#include "config.h" - -typedef struct { - uint8_t r, g, b; -} rgb_t; - -typedef struct { - uint32_t magic, seq; - uint16_t width, height; - rgb_t data[DISPLAY_WIDTH*DISPLAY_HEIGHT]; -} ml_packet_t; - -#define UDP_BUF_SIZE sizeof(ml_packet_t) - -#endif//__NET_H__ diff --git a/host/server.py b/host/server.py index ab3f7b5..456eedb 100755 --- a/host/server.py +++ b/host/server.py @@ -1,22 +1,22 @@ #!/usr/bin/env python from socketserver import * +import socket +import struct +import zlib from time import time, strftime, sleep -from collections import namedtuple -from itertools import product +from collections import namedtuple, deque +import itertools import threading import random +import os -from ctypes import CDLL, POINTER, c_void_p, Structure, c_uint8, c_size_t, cast, addressof - -import numpy as np - -from matelight import sendframe, DISPLAY_WIDTH, DISPLAY_HEIGHT +from ctypes import * +from matelight import sendframe, DISPLAY_WIDTH, DISPLAY_HEIGHT, FRAME_SIZE UDP_TIMEOUT = 3.0 - class COLOR(Structure): _fields_ = [('r', c_uint8), ('g', c_uint8), ('b', c_uint8), ('a', c_uint8)] @@ -26,134 +26,151 @@ class FRAMEBUFFER(Structure): bdf = CDLL('./libbdf.so') bdf.read_bdf_file.restype = c_void_p bdf.framebuffer_render_text.restype = POINTER(FRAMEBUFFER) +bdf.framebuffer_render_text.argtypes= [c_char_p, c_void_p, c_void_p, c_size_t, c_size_t, c_size_t] unifont = bdf.read_bdf_file('unifont.bdf') -def render_text(text): +def compute_text_bounds(text): assert unifont - fb = bdf.framebuffer_render_text(bytes(str(text), 'UTF-8'), unifont) - fbd = fb.contents - buf = np.ctypeslib.as_array(cast(fbd.data, POINTER(c_uint8)), shape=(fbd.h, fbd.w, 4)) - # Set data pointer to NULL before freeing framebuffer struct to prevent free_framebuffer from also freeing the data - # buffer that is now used by numpy - #bdf.console_render_buffer(fb) - fbd.data = cast(c_void_p(), POINTER(COLOR)) - bdf.free_framebuffer(fb) - return buf + textbytes = bytes(str(text), 'UTF-8') + textw, texth = c_size_t(0), c_size_t(0) + res = bdf.framebuffer_get_text_bounds(textbytes, unifont, byref(textw), byref(texth)) + if res: + raise ValueError('Invalid text') + return textw.value, texth.value + +cbuf = create_string_buffer(FRAME_SIZE*sizeof(COLOR)) +cbuflock = threading.Lock() +def render_text(text, offset): + global cbuf + cbuflock.acquire() + textbytes = bytes(str(text), 'UTF-8') + res = bdf.framebuffer_render_text(textbytes, unifont, cbuf, DISPLAY_WIDTH, DISPLAY_HEIGHT, offset) + if res: + raise ValueError('Invalid text') + cbuflock.release() + return cbuf + +printlock = threading.Lock() def printframe(fb): - h,w,_ = fb.shape - print('\033[s\033[H', end='') - bdf.console_render_buffer(fb.ctypes.data_as(POINTER(c_uint8)), w, h) - print('\033[0m\033[u', end='') - -def scroll(text): - """ Returns whether it could scroll all the text uninterrupted """ - fb = render_text(text); - h,w,_ = fb.shape - for i in range(-DISPLAY_WIDTH,w+1): - if current_entry.entrytype == 'udp' or (current_entry in defaulttexts and textqueue): - return False - leftpad = np.zeros((DISPLAY_HEIGHT, max(-i, 0), 4), dtype=np.uint8) - framedata = fb[:, max(0, i):min(i+DISPLAY_WIDTH, w)] - rightpad = np.zeros((DISPLAY_HEIGHT, min(DISPLAY_WIDTH, max(0, i+DISPLAY_WIDTH-w)), 4), dtype=np.uint8) - dice = np.concatenate((leftpad, framedata, rightpad), 1) - sendframe(dice) - #printframe(dice) - fb = render_text(text); - return True - -QueueEntry = namedtuple('QueueEntry', ['entrytype', 'remote', 'timestamp', 'text']) -defaulttexts = [QueueEntry('text', '127.0.0.1', 0, t) for t in [ - '\x1B[92mMate Light\x1B[93m@\x1B[92mPlay store or \x1B[94;101mtcp://matelight.cbrp3.c-base.org:1337\x1B[0;91m ♥', - '\x1B[92mMate Light\x1B[0;91m ♥ \x1B[92mUnicode', - '\x1B[92mMate Light\x1B[0m powered by \x1B[95mMicrosoft™ \x1B[96mMarquee Manager® Professional OEM', - '\x1B[92mMate Light\x1B[0m powered by \x1B[95mData Becker™ \x1B[96mLaufschriftstudio 2000® Platinum Edition', - '\x1B[92mMate Light\x1B[0m powered by \x1B[95mApple™ \x1B[96miScroll®', - '\x1B[92mMate Light\x1B[0m powered by \x1B[95mSiemens™ \x1B[96mLaufschrift® v.0.1.2b fuer Intel™ Pentium®', - ]] -current_entry = random.choice(defaulttexts) -conns = {} -textqueue = [] + printlock.acquire() + print('\0337\033[H', end='') + print('Rendering frame @{}'.format(time())) + bdf.console_render_buffer(fb, DISPLAY_WIDTH, DISPLAY_HEIGHT) + #print('\033[0m\033[KCurrently rendering', current_entry.entrytype, 'from', current_entry.remote, ':', current_entry.text, '\0338', end='') + printlock.release() def log(*args): - print(strftime('[%m-%d %H:%M:%S]'), *args) + printlock.acquire() + print(strftime('\x1B[93m[%m-%d %H:%M:%S]\x1B[0m'), ' '.join(str(arg) for arg in args), '\x1B[0m') + printlock.release() + +class TextRenderer: + def __init__(self, text): + self.text = text + self.width, _ = compute_text_bounds(text) + + def __iter__(self): + for i in range(-DISPLAY_WIDTH, self.width): + yield render_text(self.text, i) + +class MateLightUDPServer: + def __init__(self, port=1337, ip=''): + self.current_client = None + self.last_timestamp = 0 + self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.socket.bind((ip, port)) + self.thread = threading.Thread(target = self.udp_receive) + self.thread.daemon = True + self.start = self.thread.start + self.frame_condition = threading.Condition() + self.frame = None + + def frame_da(self): + return self.frame is not None + + def __iter__(self): + while True: + with self.frame_condition: + if not self.frame_condition.wait_for(self.frame_da, timeout=UDP_TIMEOUT): + raise StopIteration() + frame, self.frame = self.frame, None + yield frame + + def udp_receive(self): + while True: + try: + data, (addr, sport) = self.socket.recvfrom(FRAME_SIZE*3+4) + timestamp = time() + if timestamp - self.last_timestamp > UDP_TIMEOUT: + self.current_client = addr + log('\x1B[91mAccepting UDP data from\x1B[0m', addr) + if addr == self.current_client: + if len(data) == FRAME_SIZE*3+4: + frame = data[:-4] + crc1, = struct.unpack('!I', data[-4:]) + if crc1: + crc2, = zlib.crc32(frame, 0), + if crc1 != crc2: + raise ValueError('Invalid frame CRC checksum: Expected {}, got {}'.format(crc2, crc1)) + elif len(data) == FRAME_SIZE*3: + frame = data + else: + raise ValueError('Invalid frame size: {}'.format(len(data))) + self.last_timestamp = timestamp + with self.frame_condition: + self.frame = frame + self.frame_condition.notify() + except Exception as e: + log('Error receiving UDP frame:', e) -class MateLightUDPHandler(BaseRequestHandler): - def handle(self): - try: - global current_entry, conns - data = self.request[0].strip() - if len(data) != FRAME_SIZE+4: - #raise ValueError('Invalid frame size: Expected {}, got {}'.format(FRAME_SIZE+4, len(data))) - return - frame = data[:-4] - #crc1, = struct.unpack('!I', data[-4:]) - #crc2, = zlib.crc32(frame, 0), - #if crc1 != crc2: - # raise ValueError('Invalid frame CRC checksum: Expected {}, got {}'.format(crc2, crc1)) - #socket.sendto(b'ACK', self.client_address) - a = np.array(list(frame), dtype=np.uint8) - timestamp = time() - addr = self.client_address[0] - if addr not in conns: - current_entry = QueueEntry('udp', addr, timestamp, '') - conns[addr] = current_entry - log('New UDP connection from', addr) - else: - conns[addr].timestamp = timestamp - if current_entry.entrytype == 'udp' and current_entry.remote == addr: - frame = a.reshape((DISPLAY_HEIGHT, DISPLAY_WIDTH, 3)) - sendframe(frame) - #printframe(frame) - except Exception as e: - print('Error receiving UDP frame:', e) - ex_type, ex, tb = sys.exc_info() - traceback.print_tb(tb) +renderqueue = deque() class MateLightTCPTextHandler(BaseRequestHandler): def handle(self): - global current_entry, conns + global render_deque data = str(self.request.recv(1024).strip(), 'UTF-8') addr = self.client_address[0] - timestamp = time() if len(data) > 140: self.request.sendall('TOO MUCH INFORMATION!\n') return - log('Text from {}: {}\x1B[0m'.format(addr, data)) - textqueue.append(QueueEntry('text', addr, timestamp, data)) + log('\x1B[95mText from\x1B[0m {}: {}\x1B[0m'.format(addr, data)) + renderqueue.append(TextRenderer(data)) self.request.sendall(b'KTHXBYE!\n') TCPServer.allow_reuse_address = True -server = TCPServer(('', 1337), MateLightTCPTextHandler) -t = threading.Thread(target=server.serve_forever) +tserver = TCPServer(('', 1337), MateLightTCPTextHandler) +t = threading.Thread(target=tserver.serve_forever) t.daemon = True t.start() -UDPServer.allow_reuse_address = True -userver = UDPServer(('', 1337), MateLightUDPHandler) -t = threading.Thread(target=userver.serve_forever) -t.daemon = True -t.start() +userver = MateLightUDPServer() +userver.start() + +defaultlines = [ TextRenderer(l[:-1].replace('\\x1B', '\x1B')) for l in open('default.lines').readlines() ] +random.shuffle(defaultlines) +defaulttexts = itertools.cycle(itertools.chain(*defaultlines)) if __name__ == '__main__': - print('\n'*7) + #print('\033[?1049h'+'\n'*9) while True: - if current_entry.entrytype == 'text': - if scroll(current_entry.text): - textqueue.remove(current_entry) - if textqueue: - current_entry = textqueue[0] - else: - if conns: - current_entry = random.choice(conns.values()) - else: - current_entry = random.choice(defaulttexts) - if current_entry.entrytype != 'udp' and textqueue: - current_entry = textqueue[0] - if current_entry.entrytype == 'udp': - if time() - current_entry.timestamp > UDP_TIMEOUT: - current_entry = random.choice(defaulttexts) + if renderqueue: + renderer = renderqueue.popleft() + elif userver.frame_da(): + renderer = userver + else: + static_noise = False #time() % 300 < 60 + if static_noise: + foo = os.urandom(640) + frame = bytes([v for c in zip(list(foo), list(foo), list(foo)) for v in c ]) else: - sleep(0.2) + frame = next(defaulttexts) + sendframe(frame) + #printframe(next(defaulttexts)) + continue + for frame in renderer: + sendframe(frame) + #printframe(frame) diff --git a/host/terminal.py b/host/terminal.py new file mode 100644 index 0000000..86b4db1 --- /dev/null +++ b/host/terminal.py @@ -0,0 +1,19 @@ +from pixelterm.pixelterm import termify_pixels + +class MockImage: + def __init__(self, nparray): + self.nparray = nparray + self.im = self + + @property + def size(self): + h, w, _ = self.nparray.shape + return (w, h) + + def getpixel(self, pos): + x, y = pos + return tuple(self.nparray[y][x]) + +def printframe(framedata): + print(termify_pixels(MockImage(framedata))) + diff --git a/host/usb.c b/host/usb.c new file mode 100644 index 0000000..5e696d5 --- /dev/null +++ b/host/usb.c @@ -0,0 +1,161 @@ + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <libusb-1.0/libusb.h> + +#include "color.h" +#include "usb.h" + +int matelight_usb_init(){ + int rc = 0; + rc = libusb_init(NULL); + if(rc){ + fprintf(stderr, "LIBML: Cannot initialize libusb\n"); + return 1; + } + return 0; +} + +void matelight_usb_destroy(){ + libusb_exit(NULL); +} + +static int matelight_cmp_str_desc(libusb_device_handle *dev, uint8_t index, char *value){ + char data[256]; + if(libusb_get_string_descriptor_ascii(dev, index, (unsigned char*)data, sizeof(data)) < 0){ + fprintf(stderr, "LIBML: Cannot read device string descriptor\n"); + return 0; + } + return strcmp(data, value) == 0; +} + +matelight_handle *matelight_open(){ + matelight_handle *out = NULL; + libusb_device** device_list; + struct libusb_device_descriptor desc; + ssize_t res = libusb_get_device_list(NULL, &device_list); + if(res == 0){ + fprintf(stderr, "LIBML: Cannot find any connected matelight\n"); + goto error; + }else if(res < 0){ + fprintf(stderr, "LIBML: Error enumerating connected USB devices\n"); + goto error; + }else{ + out = calloc(res+1, sizeof(matelight_handle)); + if(!out){ + fprintf(stderr, "LIBML: Cannot allocate memory\n"); + goto error; + } + memset(out, 0, (res+1)*sizeof(matelight_handle)); + unsigned int found = 0; + for(ssize_t i=0; i<res; i++){ + libusb_get_device_descriptor(device_list[i], &desc); + if(desc.idVendor == MATELIGHT_VID && desc.idProduct == MATELIGHT_PID){ + libusb_device_handle *handle; + if(libusb_open(device_list[i], &(handle))){ + fprintf(stderr, "LIBML: Cannot open Mate Light USB device\n"); + goto error; + } + out[found].handle = handle; + if(matelight_cmp_str_desc(handle, desc.iManufacturer, "Gold & Apple")) + if(matelight_cmp_str_desc(handle, desc.iProduct, "Mate Light")){ +#define BUF_SIZE 256 + char *serial = malloc(BUF_SIZE); + if(!serial){ + fprintf(stderr, "LIBML: Cannot allocate memory\n"); + goto error; + } + if(libusb_get_string_descriptor_ascii(out[found].handle, desc.iSerialNumber, (unsigned char*)serial, BUF_SIZE) < 0){ + fprintf(stderr, "LIBML: Cannot read device string descriptor\n"); + goto error; + } +#undef BUF_SIZE + out[found].serial = serial; + found++; + } + } + } + out[found].handle = NULL; + out[found].serial = NULL; + } + libusb_free_device_list(device_list, 1); + return out; +error: + if(res>0 && out){ + for(ssize_t i=0; i<res; i++){ + if(out[i].handle) + libusb_close(out[i].handle); + free(out[i].serial); + } + } + free(out); + libusb_free_device_list(device_list, 1); + return 0; +} + +typedef struct{ + uint8_t opcode; + uint8_t x, y; + rgb_t buf[CRATE_WIDTH*CRATE_HEIGHT]; +} crate_frame_t; + +int matelight_send_frame(matelight_handle *ml, void *buf, size_t w, size_t h, float brightness, int alpha){ +// fprintf(stderr, "\nFRAME START\n"); + for(size_t cy=0; cy<h; cy++){ +// fprintf(stderr, "##### NEXT ROW\n"); + for(size_t cx=0; cx<w; cx++){ + crate_frame_t frame; + frame.opcode = 0; + frame.x = cx; + frame.y = cy; +// fprintf(stderr, "CRATE %d %d\n", cx, cy); + for(size_t x=0; x<CRATE_WIDTH; x++){ + for(size_t y=0; y<CRATE_HEIGHT; y++){ + size_t dpos = y*CRATE_WIDTH + x; + size_t spos = w*CRATE_WIDTH*(cy*CRATE_HEIGHT+y) + cx*CRATE_WIDTH+x; + color_t *src; + if(alpha){ + src = (((color_t*)buf)+spos); + }else{ + src = (color_t*)(((rgb_t*)buf)+spos); + } + rgb_t *dst = frame.buf+dpos; + /* Gamma correction */ +#define GAMMA_APPLY(c) ((uint8_t)roundf(powf((c/255.0F), GAMMA) * brightness * 255)) + dst->r = GAMMA_APPLY(src->r); + dst->g = GAMMA_APPLY(src->g); + dst->b = GAMMA_APPLY(src->b); +// fprintf(stderr, "%c", ((dst->r > 1) ? '#' : '.')); +#undef GAMMA_APPLY + } +// fprintf(stderr, "\n"); + } + int transferred = 0; + int rc = libusb_bulk_transfer(ml->handle, MATELIGHT_FRAMEDATA_ENDPOINT, (unsigned char*)&frame, sizeof(frame), &transferred, MATELIGHT_TIMEOUT); + if(rc){ + fprintf(stderr, "LIBML: Cannot write frame data\n"); + return 1; + } + if(transferred != sizeof(frame)){ + fprintf(stderr, "LIBML: Could not transfer all frame data. Only transferred %d out of %d bytes.\n", transferred, sizeof(frame)); + return 1; + } + } + } + uint8_t payload = 0x01; + int transferred = 0; + int rc = libusb_bulk_transfer(ml->handle, MATELIGHT_FRAMEDATA_ENDPOINT, &payload, sizeof(uint8_t), &transferred, MATELIGHT_TIMEOUT); + if(rc){ + fprintf(stderr, "LIBML: Cannot write frame data\n"); + return 1; + } + if(transferred != sizeof(uint8_t)){ + fprintf(stderr, "LIBML: Could not transfer all frame data. Only transferred %d out of %d bytes.\n", transferred, sizeof(uint8_t)); + return 1; + } + return 0; +} + diff --git a/host/usb.h b/host/usb.h new file mode 100644 index 0000000..fee3487 --- /dev/null +++ b/host/usb.h @@ -0,0 +1,27 @@ +#ifndef __USB_H__ +#define __USB_H__ + +#include <libusb-1.0/libusb.h> + +#define MATELIGHT_VID 0x1cbe +#define MATELIGHT_PID 0x0003 + +#define CRATE_WIDTH 5 +#define CRATE_HEIGHT 4 + +#define MATELIGHT_FRAMEDATA_ENDPOINT 0x01 +#define MATELIGHT_TIMEOUT 1000 + +#define GAMMA 2.5F + +typedef struct { + libusb_device_handle *handle; + char *serial; +} matelight_handle; + +int matelight_usb_init(void); +void matelight_usb_destroy(void); +matelight_handle *matelight_open(void); +int matelight_send_frame(matelight_handle *ml, void *buf, size_t w, size_t h, float brightness, int alpha); + +#endif//__USB_H__ |