diff options
Diffstat (limited to 'CONTRIBUTING.md')
-rw-r--r-- | CONTRIBUTING.md | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..10e3ac22 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,356 @@ +# Contributing to wlroots + +Contributing just involves sending a pull request. You will probably be more +successful with your contribution if you visit +[#sway-devel](https://webchat.freenode.net/?channels=sway-devel) on +irc.freenode.net upfront and discuss your plans. + +Note: rules are made to be broken. Adjust or ignore any/all of these as you see +fit, but be prepared to justify it to your peers. + +## 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 wlroots +2. `git clone https://github.com/username/wlroots && cd wlroots` +3. `git remote add upstream https://github.com/swaywm/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. Add and commit your changes +4. `git push -u origin add-so-and-so-feature` +5. Make a pull request from your feature branch + +When you submit your pull request, your commit log should do most of the talking +when it comes to describing your changes and their motivation. In addition to +this, your pull request's comments will ideally include a test plan that the +reviewers can use to (1) demonstrate the problem on master, if applicable and +(2) verify that the problem no longer exists with your changes applied (or that +your new features work correctly). Document all of the edge cases you're aware +of so we can adequately test them - then verify the test plan yourself before +submitting. + +## 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 separated 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 separated 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. + +See [here](https://chris.beams.io/posts/git-commit/) for more details. + +## Code Review + +When your changes are submitted for review, one or more core committers will +look over them. Smaller changes might be merged with little fanfare, but larger +changes will typically see review from several people. Be prepared to receive +some feedback - you may be asked to make changes to your work. Our code review +process is: + +1. **Triage** the pull request. Do the commit messages make sense? Is a test + plan necessary and/or present? Add anyone as reviewers that you think should + be there (using the relevant GitHub feature, if you have the permissions, or + with an @mention if necessary). +2. **Review** the code. Look for code style violations, naming convention + violations, buffer overflows, memory leaks, logic errors, non-portable code + (including GNU-isms), etc. For significant changes to the public API, loop in + a couple more people for discussion. +3. **Execute** the test plan, if present. +4. **Merge** the pull request when all reviewers approve. +5. **File** follow-up tickets if appropriate. + +## Style Reference + +wlroots is written in C with a style similar to the [kernel +style](https://www.kernel.org/doc/Documentation/process/coding-style.rst), but +with a few notable differences. + +Try to keep your code conforming to C11 and POSIX as much as possible, and do +not use GNU extensions. + +### Brackets + +Brackets always go on the same line, including in functions. +Always include brackets for if/while/for, even if it's a single statement. +```c +void function(void) { + if (condition1) { + do_thing1(); + } + + if (condition2) { + do_thing2(); + } else { + do_thing3(); + } +} +``` + +### Indentation + +Indentations are a single tab. + +For long lines that need to be broken, the continuation line should be indented +with an additional tab. +If the line being broken is opening a new block (functions, if, while, etc.), +the continuation line should be indented with two tabs, so they can't be +misread as being part of the block. +```c +really_long_function(argument1, argument2, ..., + argument3, argument4); + +if (condition1 && condition2 && ... + condition3 && condition4) { + do_thing(); +} +``` + +Try to break the line in the place which you think is the most appropriate. + + +### Line Length + +Try to keep your lines under 80 columns, but you can go up to 100 if it +improves readability. Don't break lines indiscriminately, try to find nice +breaking points so your code is easy to read. + +### Names + +Global function and type names should be prefixed with `wlr_submodule_` (e.g. +`struct wlr_output`, `wlr_output_set_cursor`). For static functions and +types local to a file, the names chosen aren't as important. Local function +names shouldn't have a `wlr_` prefix. + +For include guards, use the header's filename relative to include. Uppercase +all of the characters, and replace any invalid characters with an underscore. + +### Construction/Destruction Functions + +For functions that are responsible for constructing and destructing an object, +they should be written as a pair of one of two forms: +* `init`/`finish`: These initialize/deinitialize a type, but are **NOT** +responsible for allocating it. They should accept a pointer to some +pre-allocated memory (e.g. a member of a struct). +* `create`/`destroy`: These also initialize/deinitialize, but will return a +pointer to a `malloc`ed chunk of memory, and will `free` it in `destroy`. + +A destruction function should always be able to accept a NULL pointer or a +zeroed value and exit cleanly; this simplifies error handling a lot. + +### Error Codes + +For functions not returning a value, they should return a (stdbool.h) bool to +indicated if they succeeded or not. + +### Macros + +Try to keep the use of macros to a minimum, especially if a function can do the +job. If you do need to use them, try to keep them close to where they're being +used and `#undef` them after. + +### Example + +```c +struct wlr_backend *wlr_backend_autocreate(struct wl_display *display) { + struct wlr_backend *backend; + if (getenv("WAYLAND_DISPLAY") || getenv("_WAYLAND_DISPLAY")) { + backend = attempt_wl_backend(display); + if (backend) { + return backend; + } + } + + const char *x11_display = getenv("DISPLAY"); + if (x11_display) { + return wlr_x11_backend_create(display, x11_display); + } + + // Attempt DRM+libinput + + struct wlr_session *session = wlr_session_create(display); + if (!session) { + wlr_log(WLR_ERROR, "Failed to start a DRM session"); + return NULL; + } + + int gpu = wlr_session_find_gpu(session); + if (gpu == -1) { + wlr_log(WLR_ERROR, "Failed to open DRM device"); + goto error_session; + } + + backend = wlr_multi_backend_create(session); + if (!backend) { + goto error_gpu; + } + + struct wlr_backend *libinput = wlr_libinput_backend_create(display, session); + if (!libinput) { + goto error_multi; + } + + struct wlr_backend *drm = wlr_drm_backend_create(display, session, gpu); + if (!drm) { + goto error_libinput; + } + + wlr_multi_backend_add(backend, libinput); + wlr_multi_backend_add(backend, drm); + return backend; + +error_libinput: + wlr_backend_destroy(libinput); +error_multi: + wlr_backend_destroy(backend); +error_gpu: + wlr_session_close_file(session, gpu); +error_session: + wlr_session_destroy(session); + return NULL; +} +``` + +## Wayland protocol implementation + +Each protocol generally lives in a file with the same name, usually containing +at least one struct for each interface in the protocol. For instance, +`xdg_shell` lives in `types/wlr_xdg_shell.h` and has a `wlr_xdg_surface` struct. + +### Globals + +Global interfaces generally have public constructors and destructors. Their +struct has a field holding the `wl_global` itself, a list of resources clients +created by binding to the global, a destroy signal and a `wl_display` destroy +listener. Example: + +```c +struct wlr_compositor { + struct wl_global *global; + struct wl_list resources; + … + + struct wl_listener display_destroy; + + struct { + struct wl_signal new_surface; + struct wl_signal destroy; + } events; +}; +``` + +When the destructor is called, it should emit the destroy signal, remove the +display destroy listener, destroy the `wl_global`, destroy all bound resources +and then destroy the struct. + +### Resources + +Resources are the representation of Wayland objects on the compositor side. They +generally have an associated struct, called the _object struct_, stored in their +`user_data` field. + +Object structs can be retrieved from resources via `wl_resource_get_data`. To +prevent bad casts, a safe helper function checking the type of the resource is +used: + +```c +static const struct wl_surface_interface surface_impl; + +struct wlr_surface *wlr_surface_from_resource(struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &wl_surface_interface, + &surface_impl)); + return wl_resource_get_user_data(resource); +} +``` + +### Destroying resources + +Object structs should only be destroyed when their resource is destroyed, ie. +in the resource destroy handler (set with `wl_resource_set_implementation`). +Destructor requests should only call `wl_resource_destroy`. + +The compositor should not destroy resources on its own. + +### Inert resources + +Some resources can become inert in situations described in the protocol or when +the compositor decides to get rid of them. All requests made to inert resources +should be ignored, except the destructor. This is achieved by: + +1. When the resource becomes inert: destroy the object struct and call + `wl_resource_set_user_data(resource, NULL)`. Do not destroy the resource. +2. For each request made to a resource that can be inert: add a NULL check to + ignore the request if the resource is inert. +3. When the client calls the destructor request on the resource: call + `wl_resource_destroy(resource)` as usual. +4. When the resource is destroyed, if the resource isn't inert, destroy the + object struct. + +Example: + +```c +// Handles the destroy request +static void subsurface_handle_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +// Handles a regular request +static void subsurface_set_position(struct wl_client *client, + struct wl_resource *resource, int32_t x, int32_t y) { + struct wlr_subsurface *subsurface = subsurface_from_resource(resource); + if (subsurface == NULL) { + return; + } + + … +} + +// Destroys the wlr_subsurface struct +static void subsurface_destroy(struct wlr_subsurface *subsurface) { + if (subsurface == NULL) { + return; + } + + … + + wl_resource_set_user_data(subsurface->resource, NULL); + free(subsurface); +} + +// Resource destroy listener +static void subsurface_handle_resource_destroy(struct wl_resource *resource) { + struct wlr_subsurface *subsurface = subsurface_from_resource(resource); + subsurface_destroy(subsurface); +} + +// Makes the resource inert +static void subsurface_handle_surface_destroy(struct wl_listener *listener, + void *data) { + struct wlr_subsurface *subsurface = + wl_container_of(listener, subsurface, surface_destroy); + subsurface_destroy(subsurface); +} +``` |