Merge remote-tracking branch 'upstream/master' into rotation

This commit is contained in:
Scott Anderson 2017-06-08 00:52:33 +12:00
commit 382f712792
7 changed files with 337 additions and 28 deletions

204
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,204 @@
# Contributing to wlroots
Contributing just involves sending a pull request. You will probably be more
successful with your contribution if you visit the [IRC
channel](http://webchat.freenode.net/?channels=sway&uio=d4) upfront and discuss
your plans.
## Pull Requests
If you already have your own pull request habits, feel free to use them. If you
don't, however, allow me to make a suggestion: feature branches pulled from
upstream. Try this:
1. Fork sway
2. Clone your fork
3. git remote add upstream https://github.com/SirCmpwn/wlroots
You only need to do this once. You're never going to use your fork's master
branch. Instead, when you start working on a feature, do this:
1. git fetch upstream
2. git checkout -b add-so-and-so-feature upstream/master
3. work
4. git push -u origin add-so-and-so-feature
5. Make pull request from your feature branch
## Commit Messages
Please strive to write good commit messages. Here's some guidelines to follow:
The first line should be limited to 50 characters and should be a sentence that
completes the thought [When applied, this commit will...] "Implement cmd_move"
or "Fix #742" or "Improve performance of arrange_windows on ARM" or similar.
The subsequent lines should be seperated from the subject line by a single
blank line, and include optional details. In this you can give justification
for the change, [reference Github
issues](https://help.github.com/articles/closing-issues-via-commit-messages/),
or explain some of the subtler details of your patch. This is important because
when someone finds a line of code they don't understand later, they can use the
`git blame` command to find out what the author was thinking when they wrote
it. It's also easier to review your pull requests if they're seperated into
logical commits that have good commit messages and justify themselves in the
extended commit description.
As a good rule of thumb, anything you might put into the pull request
description on Github is probably fair game for going into the extended commit
message as well.
## Coding Style
wlroots is written in C with the same style as Sway. The style guidelines is
[kernel
style](https://www.kernel.org/doc/Documentation/process/coding-style.rst), but
all braces go on the same line (*"but K&R says so!" is a silly way of justifying
something*). Some points to note:
* Do not use typedefs unless you have a good reason
* Do not use macros unless you have a *really* good reason
* Align `case` with `switch`
* Tabs, not spaces
* `char *pointer` - note position of `*`
* Use logging with reckless abandon
* Always include braces for if/for/while/etc, even for one-liners
An example of well formatted code:
```C
#include <stdio.h>
#include <stdlib.h>
#include "log.h"
#include "example.h"
struct foobar {
char *foo;
int bar;
long baz;
}; // Do not typedef without a good reason
int main(int argc, const char **argv) {
if (argc != 4) {
sway_abort("Do not run this program manually. See man 5 sway and look for output options.");
}
if (!registry->desktop_shell) {
sway_abort("swaybg requires the compositor to support the desktop-shell extension.");
}
int desired_output = atoi(argv[1]);
sway_log(L_INFO, "Using output %d of %d", desired_output, registry->outputs->length);
int i;
struct output_state *output = registry->outputs->items[desired_output];
struct window *window = window_setup(registry, 100, 100, false);
if (!window) {
sway_abort("Failed to create surfaces.");
}
window->width = output->width;
window->height = output->height;
desktop_shell_set_background(registry->desktop_shell, output->output, window->surface);
list_add(surfaces, window);
cairo_surface_t *image = cairo_image_surface_create_from_png(argv[2]);
double width = cairo_image_surface_get_width(image);
double height = cairo_image_surface_get_height(image);
const char *scaling_mode_str = argv[3];
enum scaling_mode scaling_mode;
if (strcmp(scaling_mode_str, "stretch") == 0) {
scaling_mode = SCALING_MODE_STRETCH;
} else if (strcmp(scaling_mode_str, "fill") == 0) {
scaling_mode = SCALING_MODE_FILL;
} else if (strcmp(scaling_mode_str, "fit") == 0) {
scaling_mode = SCALING_MODE_FIT;
} else if (strcmp(scaling_mode_str, "center") == 0) {
scaling_mode = SCALING_MODE_CENTER;
} else if (strcmp(scaling_mode_str, "tile") == 0) {
scaling_mode = SCALING_MODE_TILE;
} else {
sway_abort("Unsupported scaling mode: %s", scaling_mode_str);
}
for (i = 0; i < surfaces->length; ++i) {
struct window *window = surfaces->items[i];
if (window_prerender(window) && window->cairo) {
switch (scaling_mode) {
case SCALING_MODE_STRETCH:
cairo_scale(window->cairo,
(double) window->width / width,
(double) window->height / height);
cairo_set_source_surface(window->cairo, image, 0, 0);
break;
case SCALING_MODE_FILL:
{
double window_ratio = (double) window->width / window->height;
double bg_ratio = width / height;
if (window_ratio > bg_ratio) {
double scale = (double) window->width / width;
cairo_scale(window->cairo, scale, scale);
cairo_set_source_surface(window->cairo, image,
0,
(double) window->height/2 / scale - height/2);
} else {
double scale = (double) window->height / height;
cairo_scale(window->cairo, scale, scale);
cairo_set_source_surface(window->cairo, image,
(double) window->width/2 / scale - width/2,
0);
}
break;
}
case SCALING_MODE_FIT:
{
double window_ratio = (double) window->width / window->height;
double bg_ratio = width / height;
if (window_ratio > bg_ratio) {
double scale = (double) window->height / height;
cairo_scale(window->cairo, scale, scale);
cairo_set_source_surface(window->cairo, image,
(double) window->width/2 / scale - width/2,
0);
} else {
double scale = (double) window->width / width;
cairo_scale(window->cairo, scale, scale);
cairo_set_source_surface(window->cairo, image,
0,
(double) window->height/2 / scale - height/2);
}
break;
}
case SCALING_MODE_CENTER:
cairo_set_source_surface(window->cairo, image,
(double) window->width/2 - width/2,
(double) window->height/2 - height/2);
break;
case SCALING_MODE_TILE:
{
cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image);
cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
cairo_set_source(window->cairo, pattern);
break;
}
default:
sway_abort("Scaling mode '%s' not implemented yet!", scaling_mode_str);
}
cairo_paint(window->cairo);
window_render(window);
}
}
while (wl_display_dispatch(registry->display) != -1);
for (i = 0; i < surfaces->length; ++i) {
struct window *window = surfaces->items[i];
window_teardown(window);
}
list_free(surfaces);
registry_teardown(registry);
return 0;
}
```

View file

@ -1,5 +1,29 @@
# wlroots # wlroots
A set of utility libraries for Wayland compositors. Pluggable, composable modules for building a Wayland compositor.
WIP WIP - [Status](https://github.com/SirCmpwn/wlroots/issues/9)
## Building
Install dependencies:
* cmake
* wayland
* wayland-protocols
* EGL
* GLESv2
* DRM
* GBM
* libinput
* udev
* systemd (optional, for logind support)
* asciidoc (optional, for man pages)
Run these commands:
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
make
sudo make install

View file

@ -150,14 +150,14 @@ static bool display_init_renderer(struct wlr_drm_renderer *renderer,
output->gbm = gbm_surface_create(renderer->gbm, mode->width, output->gbm = gbm_surface_create(renderer->gbm, mode->width,
mode->height, GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); mode->height, GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
if (!output->gbm) { if (!output->gbm) {
wlr_log(L_ERROR, "Failed to create GBM surface for %s: %s", output->name, wlr_log(L_ERROR, "Failed to create GBM surface for %s: %s", output->wlr_output->name,
strerror(errno)); strerror(errno));
return false; return false;
} }
output->egl = wlr_egl_create_surface(&renderer->egl, output->gbm); output->egl = wlr_egl_create_surface(&renderer->egl, output->gbm);
if (output->egl == EGL_NO_SURFACE) { if (output->egl == EGL_NO_SURFACE) {
wlr_log(L_ERROR, "Failed to create EGL surface for %s", output->name); wlr_log(L_ERROR, "Failed to create EGL surface for %s", output->wlr_output->name);
return false; return false;
} }
@ -205,7 +205,7 @@ static bool wlr_drm_output_set_mode(struct wlr_output_state *output,
struct wlr_backend_state *state = struct wlr_backend_state *state =
wl_container_of(output->renderer, state, renderer); wl_container_of(output->renderer, state, renderer);
wlr_log(L_INFO, "Modesetting '%s' with '%ux%u@%u mHz'", output->name, wlr_log(L_INFO, "Modesetting '%s' with '%ux%u@%u mHz'", output->wlr_output->name,
mode->width, mode->height, mode->refresh); mode->width, mode->height, mode->refresh);
drmModeConnector *conn = drmModeGetConnector(state->fd, output->connector); drmModeConnector *conn = drmModeGetConnector(state->fd, output->connector);
@ -215,7 +215,7 @@ static bool wlr_drm_output_set_mode(struct wlr_output_state *output,
} }
if (conn->connection != DRM_MODE_CONNECTED || conn->count_modes == 0) { if (conn->connection != DRM_MODE_CONNECTED || conn->count_modes == 0) {
wlr_log(L_ERROR, "%s is not connected", output->name); wlr_log(L_ERROR, "%s is not connected", output->wlr_output->name);
goto error; goto error;
} }
@ -250,7 +250,7 @@ static bool wlr_drm_output_set_mode(struct wlr_output_state *output,
drmModeFreeResources(res); drmModeFreeResources(res);
if (!success) { if (!success) {
wlr_log(L_ERROR, "Failed to find CRTC for %s", output->name); wlr_log(L_ERROR, "Failed to find CRTC for %s", output->wlr_output->name);
goto error; goto error;
} }
@ -260,7 +260,7 @@ static bool wlr_drm_output_set_mode(struct wlr_output_state *output,
output->wlr_output->current_mode = mode; output->wlr_output->current_mode = mode;
if (!display_init_renderer(&state->renderer, output)) { if (!display_init_renderer(&state->renderer, output)) {
wlr_log(L_ERROR, "Failed to initalise renderer for %s", output->name); wlr_log(L_ERROR, "Failed to initalise renderer for %s", output->wlr_output->name);
goto error; goto error;
} }
@ -304,6 +304,87 @@ static int32_t calculate_refresh_rate(drmModeModeInfo *mode) {
return refresh; return refresh;
} }
// Constructed from http://edid.tv/manufacturer
const char *get_manufacturer(uint16_t id) {
#define ID(a, b, c) ((a & 0x1f) << 10) | ((b & 0x1f) << 5) | (c & 0x1f)
switch (id) {
case ID('A', 'A', 'A'): return "Avolites Ltd";
case ID('A', 'C', 'I'): return "Ancor Communications Inc";
case ID('A', 'C', 'R'): return "Acer Technologies";
case ID('A', 'P', 'P'): return "Apple Computer Inc";
case ID('B', 'N', 'O'): return "Bang & Olufsen";
case ID('C', 'M', 'N'): return "Chimei Innolux Corporation";
case ID('C', 'M', 'O'): return "Chi Mei Optoelectronics corp.";
case ID('C', 'R', 'O'): return "Extraordinary Technologies PTY Limited";
case ID('D', 'E', 'L'): return "Dell Inc.";
case ID('D', 'O', 'N'): return "DENON, Ltd.";
case ID('E', 'N', 'C'): return "Eizo Nanao Corporation";
case ID('E', 'P', 'H'): return "Epiphan Systems Inc.";
case ID('F', 'U', 'S'): return "Fujitsu Siemens Computers GmbH";
case ID('G', 'S', 'M'): return "Goldstar Company Ltd";
case ID('H', 'I', 'Q'): return "Kaohsiung Opto Electronics Americas, Inc.";
case ID('H', 'S', 'D'): return "HannStar Display Corp";
case ID('H', 'W', 'P'): return "Hewlett Packard";
case ID('I', 'N', 'T'): return "Interphase Corporation";
case ID('I', 'V', 'M'): return "Iiyama North America";
case ID('L', 'E', 'N'): return "Lenovo Group Limited";
case ID('M', 'A', 'X'): return "Rogen Tech Distribution Inc";
case ID('M', 'E', 'G'): return "Abeam Tech Ltd";
case ID('M', 'E', 'I'): return "Panasonic Industry Company";
case ID('M', 'T', 'C'): return "Mars-Tech Corporation";
case ID('M', 'T', 'X'): return "Matrox";
case ID('N', 'E', 'C'): return "NEC Corporation";
case ID('O', 'N', 'K'): return "ONKYO Corporation";
case ID('O', 'R', 'N'): return "ORION ELECTRIC CO., LTD.";
case ID('O', 'T', 'M'): return "Optoma Corporation";
case ID('O', 'V', 'R'): return "Oculus VR, Inc.";
case ID('P', 'H', 'L'): return "Philips Consumer Electronics Company";
case ID('P', 'I', 'O'): return "Pioneer Electronic Corporation";
case ID('P', 'N', 'R'): return "Planar Systems, Inc.";
case ID('Q', 'D', 'S'): return "Quanta Display Inc.";
case ID('S', 'A', 'M'): return "Samsung Electric Company";
case ID('S', 'E', 'C'): return "Seiko Epson Corporation";
case ID('S', 'H', 'P'): return "Sharp Corporation";
case ID('S', 'I', 'I'): return "Silicon Image, Inc.";
case ID('S', 'N', 'Y'): return "Sony";
case ID('T', 'O', 'P'): return "Orion Communications Co., Ltd.";
case ID('T', 'S', 'B'): return "Toshiba America Info Systems Inc";
case ID('T', 'S', 'T'): return "Transtream Inc";
case ID('U', 'N', 'K'): return "Unknown";
case ID('V', 'I', 'Z'): return "VIZIO, Inc";
case ID('V', 'S', 'C'): return "ViewSonic Corporation";
case ID('Y', 'M', 'H'): return "Yamaha Corporation";
default: return "Unknown";
}
#undef ID
}
/* See https://en.wikipedia.org/wiki/Extended_Display_Identification_Data for layout of EDID data.
* We don't parse the EDID properly. We just expect to receive valid data.
*/
static void parse_edid(struct wlr_output *restrict output, const uint8_t *restrict data) {
uint32_t id = (data[8] << 8) | data[9];
snprintf(output->make, sizeof(output->make), "%s", get_manufacturer(id));
output->phys_width = ((data[68] & 0xf0) << 4) | data[66];
output->phys_height = ((data[68] & 0x0f) << 8) | data[67];
for (size_t i = 72; i <= 108; i += 18) {
uint16_t flag = (data[i] << 8) | data[i + 1];
if (flag == 0 && data[i + 3] == 0xFC) {
sprintf(output->model, "%.13s", &data[i + 5]);
// Monitor names are terminated by newline if they're too short
char *nl = strchr(output->model, '\n');
if (nl) {
*nl = '\0';
}
break;
}
}
}
static void scan_property_ids(int fd, drmModeConnector *conn, static void scan_property_ids(int fd, drmModeConnector *conn,
struct wlr_output_state *output) { struct wlr_output_state *output) {
for (int i = 0; i < conn->count_props; ++i) { for (int i = 0; i < conn->count_props; ++i) {
@ -312,16 +393,18 @@ static void scan_property_ids(int fd, drmModeConnector *conn,
continue; continue;
} }
// I think this is guranteed to exist
if (strcmp(prop->name, "DPMS") == 0) { if (strcmp(prop->name, "DPMS") == 0) {
output->props.dpms = prop->prop_id; output->props.dpms = prop->prop_id;
} else if (strcmp(prop->name, "EDID") == 0) {
drmModePropertyBlobRes *edid = drmModeGetPropertyBlob(fd, conn->prop_values[i]);
if (!edid) {
drmModeFreeProperty(prop);
continue;
}
/* There may be more properties we want to get, parse_edid(output->wlr_output, edid->data);
* but since it's currently only this, we exit early
*/
drmModeFreeProperty(prop); drmModeFreePropertyBlob(edid);
break;
} }
drmModeFreeProperty(prop); drmModeFreeProperty(prop);
@ -369,8 +452,7 @@ void wlr_drm_scan_connectors(struct wlr_backend_state *state) {
output->connector = id; output->connector = id;
// TODO: Populate more wlr_output fields // TODO: Populate more wlr_output fields
// TODO: Move this to wlr_output->name // TODO: Move this to wlr_output->name
wlr_output->name = output->name; snprintf(wlr_output->name, sizeof(wlr_output->name), "%s-%"PRIu32,
snprintf(output->name, sizeof(output->name), "%s-%"PRIu32,
conn_name[conn->connector_type], conn_name[conn->connector_type],
conn->connector_type_id); conn->connector_type_id);
@ -383,7 +465,7 @@ void wlr_drm_scan_connectors(struct wlr_backend_state *state) {
scan_property_ids(state->fd, conn, output); scan_property_ids(state->fd, conn, output);
list_add(state->outputs, output); list_add(state->outputs, output);
wlr_log(L_INFO, "Found display '%s'", output->name); wlr_log(L_INFO, "Found display '%s'", wlr_output->name);
} else { } else {
output = state->outputs->items[index]; output = state->outputs->items[index];
wlr_output = output->wlr_output; wlr_output = output->wlr_output;
@ -393,7 +475,7 @@ void wlr_drm_scan_connectors(struct wlr_backend_state *state) {
if (output->state == DRM_OUTPUT_DISCONNECTED && if (output->state == DRM_OUTPUT_DISCONNECTED &&
conn->connection == DRM_MODE_CONNECTED) { conn->connection == DRM_MODE_CONNECTED) {
wlr_log(L_INFO, "'%s' connected", output->name); wlr_log(L_INFO, "'%s' connected", output->wlr_output->name);
wlr_log(L_INFO, "Detected modes:"); wlr_log(L_INFO, "Detected modes:");
for (int i = 0; i < conn->count_modes; ++i) { for (int i = 0; i < conn->count_modes; ++i) {
@ -414,12 +496,12 @@ void wlr_drm_scan_connectors(struct wlr_backend_state *state) {
} }
output->state = DRM_OUTPUT_NEEDS_MODESET; output->state = DRM_OUTPUT_NEEDS_MODESET;
wlr_log(L_INFO, "Sending modesetting signal for '%s'", output->name); wlr_log(L_INFO, "Sending modesetting signal for '%s'", output->wlr_output->name);
wl_signal_emit(&state->backend->events.output_add, wlr_output); wl_signal_emit(&state->backend->events.output_add, wlr_output);
} else if (output->state == DRM_OUTPUT_CONNECTED && } else if (output->state == DRM_OUTPUT_CONNECTED &&
conn->connection != DRM_MODE_CONNECTED) { conn->connection != DRM_MODE_CONNECTED) {
wlr_log(L_INFO, "'%s' disconnected", output->name); wlr_log(L_INFO, "'%s' disconnected", output->wlr_output->name);
wlr_drm_output_cleanup(output, false); wlr_drm_output_cleanup(output, false);
} }
@ -494,7 +576,7 @@ void wlr_drm_output_cleanup(struct wlr_output_state *output, bool restore) {
if (restore) { if (restore) {
restore_output(output, renderer->fd); restore_output(output, renderer->fd);
} }
wlr_log(L_INFO, "Emmiting destruction signal for '%s'", output->name); wlr_log(L_INFO, "Emmiting destruction signal for '%s'", output->wlr_output->name);
wl_signal_emit(&state->backend->events.output_remove, output->wlr_output); wl_signal_emit(&state->backend->events.output_remove, output->wlr_output);
break; break;
case DRM_OUTPUT_DISCONNECTED: case DRM_OUTPUT_DISCONNECTED:

View file

@ -2,6 +2,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <time.h> #include <time.h>
#include <inttypes.h>
#include <wayland-server.h> #include <wayland-server.h>
#include <GLES3/gl3.h> #include <GLES3/gl3.h>
#include <wlr/backend.h> #include <wlr/backend.h>
@ -54,6 +55,8 @@ void output_add(struct wl_listener *listener, void *data) {
struct wlr_output *output = data; struct wlr_output *output = data;
struct state *state = wl_container_of(listener, state, output_add); struct state *state = wl_container_of(listener, state, output_add);
fprintf(stderr, "Output '%s' added\n", output->name); fprintf(stderr, "Output '%s' added\n", output->name);
fprintf(stderr, "%s %s %"PRId32"mm x %"PRId32"mm\n", output->make, output->model,
output->phys_width, output->phys_height);
wlr_output_set_mode(output, output->modes->items[0]); wlr_output_set_mode(output, output->modes->items[0]);
struct output_state *ostate = calloc(1, sizeof(struct output_state)); struct output_state *ostate = calloc(1, sizeof(struct output_state));
ostate->output = output; ostate->output = output;

View file

@ -34,7 +34,6 @@ struct wlr_output_state {
struct wlr_output *wlr_output; struct wlr_output *wlr_output;
enum wlr_drm_output_state state; enum wlr_drm_output_state state;
uint32_t connector; uint32_t connector;
char name[16];
struct { struct {
uint32_t dpms; uint32_t dpms;

View file

@ -22,11 +22,10 @@ struct wlr_output {
struct wlr_output_state *state; struct wlr_output_state *state;
uint32_t flags; uint32_t flags;
char *name; char name[16];
char *make; char make[48];
char *model; char model[16];
uint32_t scale; uint32_t scale;
int32_t x, y;
int32_t width, height; int32_t width, height;
int32_t phys_width, phys_height; // mm int32_t phys_width, phys_height; // mm
int32_t subpixel; // enum wl_output_subpixel int32_t subpixel; // enum wl_output_subpixel

View file

@ -94,8 +94,6 @@ void wlr_output_transform(struct wlr_output *output,
void wlr_output_destroy(struct wlr_output *output) { void wlr_output_destroy(struct wlr_output *output) {
if (!output) return; if (!output) return;
output->impl->destroy(output->state); output->impl->destroy(output->state);
free(output->make);
free(output->model);
for (size_t i = 0; output->modes && i < output->modes->length; ++i) { for (size_t i = 0; output->modes && i < output->modes->length; ++i) {
free(output->modes->items[i]); free(output->modes->items[i]);
} }