Skip to content

Commit 1488dc4

Browse files
committed
Work on Python + C AmbiLight/Notifications example
1 parent cc0932c commit 1488dc4

File tree

11 files changed

+470
-102
lines changed

11 files changed

+470
-102
lines changed

CMakeLists.txt

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,72 @@
33
# Copyright (c) 2018-2019 RedFantom
44
cmake_minimum_required(VERSION 3.9)
55
set(CMAKE_C_FLAGS "-std=c99")
6-
include(libmk/CMakeLists.txt)
7-
if (SKBUILD) # The masterkeys package can only be built with scikit-build
8-
include(masterkeys/CMakeLists.txt)
9-
endif()
10-
include(examples/CMakeLists.txt)
11-
include(utils/CMakeLists.txt)
6+
project(libmk VERSION 0.2.0 DESCRIPTION "MasterKeys RGB Library")
7+
8+
# Dependencies
9+
set(LIBUSB_INCLUDE_DIR /usr/include/libusb-1.0)
10+
FIND_PATH(LIBUSB_INCLUDE_DIR libusb.h
11+
HINTS $ENV{LIBUSB_ROOT}
12+
PATHS ${PC_LIBUSB_INCLUDEDIR} ${PC_LIBUSB_INCLUDE_DIRS}
13+
PATH_SUFFIXES include)
14+
FIND_LIBRARY(LIBUSB_LIBRARIES NAMES usb-1.0
15+
HINTS $ENV{LIBUSB_ROOT}
16+
PATHS ${PC_LIBUSB_LIBDIR} ${PC_LIBUSB_LIBRARY_DIRS}
17+
PATH_SUFFIXES lib)
18+
message("CMake found libusb: " ${LIBUSB_INCLUDE_DIR} ", usb-1.0")
19+
find_package(X11 REQUIRED)
20+
find_package(PythonInterp REQUIRED)
21+
find_package(PythonLibs REQUIRED)
22+
find_package(PythonExtensions)
23+
24+
include_directories(${LIBUSB_INCLUDE_DIR} ${X11_INCLUDE_DIRS} libmk)
25+
link_libraries(usb-1.0 ${X11_LIBRARIES})
26+
27+
# libmk library
28+
add_library(mk SHARED libmk/libmk.c)
29+
set_target_properties(mk PROPERTIES
30+
VERSION ${PROJECT_VERSION}
31+
PUBLIC_HEADER libmk/libmk.h)
32+
add_library(mkc SHARED libmk/libmkc.c)
33+
set_target_properties(mkc PROPERTIES
34+
VERSION ${PROJECT_VERSION}
35+
PUBLIC_HEADER libmk/libmkc.h)
36+
target_link_libraries(mkc mk)
37+
install(TARGETS mk
38+
LIBRARY DESTINATION lib
39+
PUBLIC_HEADER DESTINATION include)
40+
install(TARGETS mkc
41+
LIBRARY DESTINATION lib
42+
PUBLIC_HEADER DESTINATION include)
1243

13-
include(GNUInstallDirs)
44+
# utils
45+
add_executable(main utils/main.c)
46+
target_link_libraries(main mk)
47+
add_executable(record utils/record.c)
48+
target_link_libraries(record mk)
49+
add_executable(ctrl utils/ctrl.c)
50+
target_link_libraries(ctrl mk mkc)
51+
52+
# examples
53+
add_executable(ambilight examples/ambilight/ambilight.c)
54+
target_link_libraries(ambilight mk pthread)
55+
56+
project(mk_notifications)
57+
add_library(mk_notifications MODULE
58+
examples/notifications/mk_notifications.c)
59+
python_extension_module(mk_notifications)
60+
set_target_properties(mk_notifications PROPERTIES
61+
OUTPUT_NAME "mk_notifications")
62+
63+
# masterkeys Python module
64+
if (SKBUILD) # python setup.py
65+
project(masterkeys VERSION 0.2.0 DESCRIPTION "Wrapper around libmk")
66+
add_library(masterkeys MODULE
67+
masterkeys/masterkeys.c
68+
libmk/libmk.c libmk/libmk.h)
69+
python_extension_module(masterkeys)
70+
target_link_libraries(masterkeys ${PYTHON_LIBRARIES})
71+
set_target_properties(masterkeys PROPERTIES
72+
OUTPUT_NAME "masterkeys")
73+
install(TARGETS masterkeys LIBRARY DESTINATION masterkeys)
74+
endif()

FindLibUSB.cmake

Lines changed: 0 additions & 16 deletions
This file was deleted.

examples/CMakeLists.txt

Lines changed: 0 additions & 8 deletions
This file was deleted.

examples/ambilight/CMakeLists.txt

Lines changed: 0 additions & 15 deletions
This file was deleted.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Author: RedFantom
2+
# License: GNU GPLv3
3+
# Copyright (c) 2018-2019 RedFantom
4+
project(mk_notifications VERSION "0.1.0")
5+
find_package(PythonInterp REQUIRED)
6+
find_package(PythonLibs REQUIRED)
7+
find_package(PythonExtensions)
8+
add_library(mk_notifications MODULE
9+
examples/notifications/mk_notifications.c)
10+
python_extension_module(mk_notifications)
11+
12+
message(${CMAKE_CURRENT_LIST_DIR})
13+
message(${CMAKE_CURRENT_SOURCE_DIR})
14+
15+
target_include_directories(masterkeys PUBLIC
16+
${PYTHON_INCLUDE_DIRS}
17+
libmk)
18+
target_link_libraries(masterkeys ${PYTHON_LIBRARIES} mk usb-1.0 pthread)
19+
20+
set_target_properties(masterkeys PROPERTIES
21+
OUTPUT_NAME "masterkeys"
22+
PREFIX "")

examples/notifications/capture.h

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
/** Author: RedFantom
2+
* License: GNU GPLv3
3+
* Copyright (c) 2018-2019 RedFantom
4+
*/
5+
#include <pthread.h>
6+
#include <stdbool.h>
7+
#include <stdlib.h>
8+
#include <X11/Xlib.h>
9+
#include <X11/X.h>
10+
11+
12+
typedef struct CaptureArgs {
13+
unsigned char* target_color;
14+
pthread_mutex_t* target_lock;
15+
pthread_mutex_t* exit_lock;
16+
bool* exit_flag;
17+
Display* display;
18+
Window root;
19+
XWindowAttributes gwa;
20+
int divider;
21+
int saturation_bias;
22+
int upper_threshold;
23+
int lower_threshold;
24+
bool brightness_norm;
25+
} CaptureArgs;
26+
27+
28+
typedef struct Screenshot {
29+
unsigned char* data;
30+
unsigned int w, h;
31+
} Screenshot;
32+
33+
34+
CaptureArgs* init_capture(int divider, int sat_bias, int lower, int upper,
35+
bool brightness_norm,
36+
unsigned char* target_color, pthread_mutex_t* target_lock,
37+
bool* exit_flag, pthread_mutex_t* exit_lock) {
38+
/** Initialize a CaptureArgs struct that can be passed as thread argument */
39+
CaptureArgs* args = (CaptureArgs*) malloc(sizeof(CaptureArgs));
40+
41+
args->divider = divider;
42+
args->saturation_bias = sat_bias;
43+
args->lower_threshold = lower;
44+
args->upper_threshold = upper;
45+
args->brightness_norm = brightness_norm;
46+
args->target_color = target_color;
47+
args->target_lock = target_lock;
48+
args->exit_flag = exit_flag;
49+
args->exit_lock = exit_lock;
50+
51+
args->display = XOpenDisplay(NULL);
52+
args->root = DefaultRootWindow(args->display);
53+
if (XGetWindowAttributes(args->display, args->root, &(args->gwa)) < 0) {
54+
XCloseDisplay(args->display);
55+
free(args);
56+
return NULL;
57+
}
58+
59+
return args;
60+
}
61+
62+
63+
void capturer(struct CaptureArgs* args) {
64+
/** Function designed to be run in a thread, captures screenshots
65+
*
66+
*/
67+
while (true) {
68+
pthread_mutex_lock(args->exit_lock);
69+
bool exit = *(args->exit_flag);
70+
pthread_mutex_unlock(args->exit_lock);
71+
if (exit)
72+
break;
73+
74+
unsigned char target[3];
75+
Screenshot* screenshot;
76+
77+
capture(&screenshot, args->gwa, args->display, args->root);
78+
calc_dominant_color(screenshot->data, screenshot->w, screenshot->h,
79+
target, args->divider, args->saturation_bias,
80+
args->lower_threshold, args->upper_threshold,
81+
args->brightness_norm);
82+
83+
free(screenshot->data);
84+
free(screenshot);
85+
86+
pthread_mutex_lock(args->target_lock);
87+
for (int i=0; i<3; i++)
88+
args->target_color[i] = target[i];
89+
pthread_mutex_unlock(args->target_lock);
90+
}
91+
}
92+
93+
94+
void capture(Screenshot** screenshot, XWindowAttributes gwa,
95+
Display* display, Window root) {
96+
/** Capture screenshot and save it to Screenshot struct
97+
*
98+
* The data captured from X.org is in long int format (32-bit
99+
* integers), which have to be converted to three separate 8-bit
100+
* integers representing the pixels.
101+
*/
102+
(*screenshot) = (Screenshot*) malloc(sizeof(Screenshot));
103+
int width = gwa.width, height = gwa.height;
104+
105+
(*screenshot)->data = (unsigned char*) malloc(
106+
width*height*3*sizeof(unsigned char));
107+
XImage* img = XGetImage(
108+
display, root, 0, 0, width, height, AllPlanes, ZPixmap);
109+
unsigned long masks[3] = {
110+
img->red_mask, img->green_mask, img->blue_mask};
111+
112+
for (int x=0; x<width; x++)
113+
for (int y=0; y<height; y++) {
114+
unsigned long pix = XGetPixel(img, x, y);
115+
for (int i=0; i<3; i++)
116+
(*screenshot)->data[(x+width*y) * 3 + i] =
117+
(unsigned char) (pix >> (2-i) * 8);
118+
}
119+
120+
XDestroyImage(img);
121+
}
122+
123+
124+
void calc_dominant_color(unsigned char* data, int w, int h,
125+
unsigned char* target, int divider, int sat_bias,
126+
int lower, int upper, bool brightness_norm) {
127+
/** Calculate the dominant color in an array of pixels
128+
*
129+
*
130+
*/
131+
unsigned char temp[3];
132+
unsigned long colors[3] = {0};
133+
unsigned long n_pixels = 0;
134+
135+
int width = w / divider;
136+
137+
/// Summing of pixels fitting criteria
138+
for (int x=0; x<width; x++) {
139+
for (int y=0; y<h; y++) {
140+
unsigned int sum = 0;
141+
unsigned char r, g, b;
142+
int max_diff = 0;
143+
unsigned char* pixel = data[x+y*w];
144+
for (int i=0; i<3; i++) {
145+
int first = i, second = i+1<3 ? i+1 : 0;
146+
unsigned char diff = pixel[first] - pixel[second];
147+
max_diff = diff > max_diff ? diff : max_diff;
148+
sum += pixel[i];
149+
}
150+
if (sum < lower || sum > upper || max_diff < sat_bias)
151+
continue;
152+
for (int i=0; i<3; i++)
153+
colors[i] += pixel[i];
154+
n_pixels += 1;
155+
}
156+
}
157+
158+
/// Averaging
159+
unsigned char color[3];
160+
unsigned char max = 0;
161+
for (int i=0; i<3; i++) {
162+
if (n_pixels == 0) {
163+
target[i] = 0xFF; // Error condition
164+
continue;
165+
}
166+
target[i] = (unsigned char) (colors[i] / n_pixels);
167+
max = color[i] > max ? color[i] : max;
168+
}
169+
170+
/// Normalization
171+
if (brightness_norm && max != 0xFF)
172+
for (int i=0; i<3; i++)
173+
target[i] = (unsigned char) ((double) color[i] * (255.0 / (double) max));
174+
}

0 commit comments

Comments
 (0)