294 lines
6.3 KiB
C
294 lines
6.3 KiB
C
/*
|
|
* Copyright 2014 The Chromium OS Authors. All rights reserved.
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
#include <assert.h>
|
|
#include <pthread.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <xcb/xcb.h>
|
|
|
|
#include "simulation.h"
|
|
|
|
/*****************************************************************************/
|
|
/* Window drawing stuff */
|
|
|
|
/* Dimensions - may change */
|
|
static int win_w = 1024;
|
|
static int win_h = 32;
|
|
|
|
static xcb_connection_t *c;
|
|
static xcb_screen_t *screen;
|
|
static xcb_drawable_t win;
|
|
static xcb_gcontext_t foreground;
|
|
static xcb_colormap_t colormap_id;
|
|
|
|
static int fake_power;
|
|
|
|
void init_windows(void)
|
|
{
|
|
uint32_t mask = 0;
|
|
uint32_t values[2];
|
|
|
|
/* Open the connection to the X server */
|
|
c = xcb_connect(NULL, NULL);
|
|
|
|
/* Get the first screen */
|
|
screen = xcb_setup_roots_iterator(xcb_get_setup(c)).data;
|
|
|
|
/* Get a colormap */
|
|
colormap_id = xcb_generate_id(c);
|
|
xcb_create_colormap(c, XCB_COLORMAP_ALLOC_NONE,
|
|
colormap_id, screen->root, screen->root_visual);
|
|
|
|
/* Create foreground GC */
|
|
foreground = xcb_generate_id(c);
|
|
mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES;
|
|
values[0] = screen->white_pixel;
|
|
values[1] = 0;
|
|
xcb_create_gc(c, foreground, screen->root, mask, values);
|
|
|
|
/* Create the window */
|
|
win = xcb_generate_id(c);
|
|
mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
|
|
values[0] = screen->black_pixel;
|
|
values[1] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS;
|
|
xcb_create_window(c, /* Connection */
|
|
XCB_COPY_FROM_PARENT, /* depth */
|
|
win, /* window Id */
|
|
screen->root, /* parent window */
|
|
0, 0, /* x, y */
|
|
win_w, win_h, /* width, height */
|
|
10, /* border_width */
|
|
XCB_WINDOW_CLASS_INPUT_OUTPUT, /* class */
|
|
screen->root_visual, /* visual */
|
|
mask, values); /* masks */
|
|
|
|
/* Map the window on the screen */
|
|
xcb_map_window(c, win);
|
|
|
|
/* We flush the request */
|
|
xcb_flush(c);
|
|
}
|
|
|
|
void cleanup(void)
|
|
{
|
|
xcb_destroy_window(c, win);
|
|
xcb_free_gc(c, foreground);
|
|
xcb_free_colormap(c, colormap_id);
|
|
xcb_disconnect(c);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Draw the lightbar elements */
|
|
|
|
/* xcb likes 16-bit colors */
|
|
uint16_t leds[NUM_LEDS][3] = {
|
|
{0xffff, 0x0000, 0x0000},
|
|
{0x0000, 0xffff, 0x0000},
|
|
{0x0000, 0x0000, 0xffff},
|
|
{0xffff, 0xffff, 0x0000},
|
|
};
|
|
pthread_mutex_t leds_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
void change_gc_color(uint16_t red, uint16_t green, uint16_t blue)
|
|
{
|
|
uint32_t mask = 0;
|
|
uint32_t values[2];
|
|
xcb_alloc_color_reply_t *reply;
|
|
|
|
reply = xcb_alloc_color_reply(c,
|
|
xcb_alloc_color(c, colormap_id,
|
|
red, green, blue),
|
|
NULL);
|
|
assert(reply);
|
|
|
|
mask = XCB_GC_FOREGROUND;
|
|
values[0] = reply->pixel;
|
|
xcb_change_gc(c, foreground, mask, values);
|
|
free(reply);
|
|
}
|
|
|
|
void update_window(void)
|
|
{
|
|
xcb_segment_t segments[] = {
|
|
{0, 0, win_w, win_h},
|
|
{0, win_h, win_w, 0},
|
|
};
|
|
xcb_rectangle_t rect;
|
|
int w = win_w / NUM_LEDS;
|
|
int i;
|
|
uint16_t copyleds[NUM_LEDS][3];
|
|
|
|
if (fake_power) {
|
|
pthread_mutex_lock(&leds_mutex);
|
|
memcpy(copyleds, leds, sizeof(leds));
|
|
pthread_mutex_unlock(&leds_mutex);
|
|
|
|
for (i = 0; i < NUM_LEDS; i++) {
|
|
rect.x = i * w;
|
|
rect.y = 0;
|
|
rect.width = w;
|
|
rect.height = win_h;
|
|
|
|
change_gc_color(copyleds[i][0],
|
|
copyleds[i][1],
|
|
copyleds[i][2]);
|
|
|
|
xcb_poly_fill_rectangle(c, win, foreground, 1, &rect);
|
|
}
|
|
} else {
|
|
rect.x = 0;
|
|
rect.y = 0;
|
|
rect.width = win_w;
|
|
rect.height = win_h;
|
|
|
|
change_gc_color(0, 0, 0);
|
|
xcb_poly_fill_rectangle(c, win, foreground, 1, &rect);
|
|
|
|
change_gc_color(0x8080, 0, 0);
|
|
|
|
for (i = 0; i < NUM_LEDS; i++) {
|
|
segments[0].x1 = i * w;
|
|
segments[0].y1 = 0;
|
|
segments[0].x2 = segments[0].x1 + w;
|
|
segments[0].y2 = win_h;
|
|
segments[1].x1 = segments[0].x1;
|
|
segments[1].y1 = win_h;
|
|
segments[1].x2 = segments[0].x2;
|
|
segments[1].y2 = 0;
|
|
xcb_poly_segment(c, win, foreground, 2, segments);
|
|
}
|
|
}
|
|
|
|
xcb_flush(c);
|
|
}
|
|
|
|
void setrgb(int led, int red, int green, int blue)
|
|
{
|
|
led %= NUM_LEDS;
|
|
|
|
pthread_mutex_lock(&leds_mutex);
|
|
leds[led][0] = red << 8 | red;
|
|
leds[led][1] = green << 8 | green;
|
|
leds[led][2] = blue << 8 | blue;
|
|
pthread_mutex_unlock(&leds_mutex);
|
|
|
|
update_window();
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* lb_common stubs */
|
|
|
|
|
|
|
|
/* Brightness serves no purpose here. It's automatic on the Chromebook. */
|
|
static int brightness = 0xc0;
|
|
void lb_set_brightness(unsigned int newval)
|
|
{
|
|
brightness = newval;
|
|
}
|
|
uint8_t lb_get_brightness(void)
|
|
{
|
|
return brightness;
|
|
}
|
|
|
|
void lb_set_rgb(unsigned int led, int red, int green, int blue)
|
|
{
|
|
int i;
|
|
if (led >= NUM_LEDS)
|
|
for (i = 0; i < NUM_LEDS; i++)
|
|
setrgb(i, red, green, blue);
|
|
else
|
|
setrgb(led, red, green, blue);
|
|
}
|
|
|
|
int lb_get_rgb(unsigned int led, uint8_t *red, uint8_t *green, uint8_t *blue)
|
|
{
|
|
led %= NUM_LEDS;
|
|
pthread_mutex_lock(&leds_mutex);
|
|
*red = leds[led][0];
|
|
*green = leds[led][1];
|
|
*blue = leds[led][2];
|
|
pthread_mutex_unlock(&leds_mutex);
|
|
return 0;
|
|
}
|
|
|
|
void lb_init(void)
|
|
{
|
|
if (fake_power)
|
|
lb_set_rgb(NUM_LEDS, 0, 0, 0);
|
|
};
|
|
void lb_off(void)
|
|
{
|
|
fake_power = 0;
|
|
update_window();
|
|
};
|
|
void lb_on(void)
|
|
{
|
|
fake_power = 1;
|
|
update_window();
|
|
};
|
|
void lb_hc_cmd_dump(struct ec_response_lightbar *out)
|
|
{
|
|
printf("lightbar is %s\n", fake_power ? "on" : "off");
|
|
memset(out, fake_power, sizeof(*out));
|
|
};
|
|
void lb_hc_cmd_reg(const struct ec_params_lightbar *in) { };
|
|
|
|
int lb_power(int enabled)
|
|
{
|
|
return fake_power;
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* Event handling stuff */
|
|
|
|
void *entry_windows(void *ptr)
|
|
{
|
|
xcb_generic_event_t *e;
|
|
xcb_expose_event_t *ev;
|
|
xcb_button_press_event_t *bv;
|
|
int chg = 1;
|
|
|
|
while ((e = xcb_wait_for_event(c))) {
|
|
|
|
switch (e->response_type & ~0x80) {
|
|
case XCB_EXPOSE:
|
|
ev = (xcb_expose_event_t *)e;
|
|
if (win_w != ev->width || win_h != ev->height) {
|
|
win_w = ev->width;
|
|
win_h = ev->height;
|
|
}
|
|
update_window();
|
|
break;
|
|
case XCB_BUTTON_PRESS:
|
|
bv = (xcb_button_press_event_t *)e;
|
|
switch (bv->detail) {
|
|
case 1:
|
|
demo_battery_level(-1);
|
|
break;
|
|
case 3:
|
|
demo_battery_level(+1);
|
|
break;
|
|
case 2:
|
|
chg = !chg;
|
|
demo_is_charging(chg);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
free(e);
|
|
}
|
|
|
|
cleanup();
|
|
exit(0);
|
|
return 0;
|
|
}
|