diff options
Diffstat (limited to 'CONTRIBUTING.md')
-rw-r--r-- | CONTRIBUTING.md | 363 |
1 files changed, 188 insertions, 175 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f450563a..a5a5111f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md | |||
@@ -1,30 +1,12 @@ | |||
1 | # Contributing to sway | 1 | # Contributing to sway |
2 | 2 | ||
3 | Contributing just involves sending a pull request. You will probably be more | 3 | Contributing just involves sending a pull request. You will probably be more |
4 | successful with your contribution if you visit the [IRC | 4 | successful with your contribution if you visit |
5 | channel](http://webchat.freenode.net/?channels=sway-devel&uio=d4) upfront and discuss | 5 | [#sway-devel](https://webchat.freenode.net/?channels=sway-devel) on |
6 | your plans. | 6 | irc.freenode.net upfront and discuss your plans. |
7 | 7 | ||
8 | ## Release Cycle | 8 | Note: rules are made to be broken. Adjust or ignore any/all of these as you see |
9 | 9 | fit, but be prepared to justify it to your peers. | |
10 | The master branch of sway is always working towards becoming the next release. | ||
11 | That release will go through each of these three stages: | ||
12 | |||
13 | **In development**: during this time the release lives in the master branch and | ||
14 | is considered unstable. All pull requests merged during this time will land in | ||
15 | the release. Only developers are encouraged to run this version. | ||
16 | |||
17 | **Release candidate**: at some point (usually when development is fairly quiet), | ||
18 | SirCmpwn will announce an upcoming release candidate, often 2 weeks in | ||
19 | advance. When the two weeks are up, a branch is cut (i.e. 0.8-rc1) and from | ||
20 | that point only bugfixes land in this branch. Each week, if bugfixes landed | ||
21 | during the week, a new RC is cut. During the RC phase, more adventurous users | ||
22 | are encouraged to upgrade and start looking for and reporting bugs (especially | ||
23 | in new features). | ||
24 | |||
25 | **Stable release**: when no substantial changes are merged into an RC for one | ||
26 | week, it's released as a new stable version of sway. At this point, all users | ||
27 | are encouraged to upgrade. | ||
28 | 10 | ||
29 | ## Pull Requests | 11 | ## Pull Requests |
30 | 12 | ||
@@ -33,34 +15,44 @@ don't, however, allow me to make a suggestion: feature branches pulled from | |||
33 | upstream. Try this: | 15 | upstream. Try this: |
34 | 16 | ||
35 | 1. Fork sway | 17 | 1. Fork sway |
36 | 2. Clone your fork | 18 | 2. `git clone https://github.com/username/sway && cd sway` |
37 | 3. git remote add upstream git://github.com/swaywm/sway.git | 19 | 3. `git remote add upstream https://github.com/swaywm/sway` |
38 | 20 | ||
39 | You only need to do this once. You're never going to use your fork's master | 21 | You only need to do this once. You're never going to use your fork's master |
40 | branch. Instead, when you start working on a feature, do this: | 22 | branch. Instead, when you start working on a feature, do this: |
41 | 23 | ||
42 | 1. git fetch upstream | 24 | 1. `git fetch upstream` |
43 | 2. git checkout -b add-so-and-so-feature upstream/master | 25 | 2. `git checkout -b add-so-and-so-feature upstream/master` |
44 | 3. work | 26 | 3. Add and commit your changes |
45 | 4. git push -u origin add-so-and-so-feature | 27 | 4. `git push -u origin add-so-and-so-feature` |
46 | 5. Make pull request from your feature branch | 28 | 5. Make a pull request from your feature branch |
29 | |||
30 | When you submit your pull request, your commit log should do most of the talking | ||
31 | when it comes to describing your changes and their motivation. In addition to | ||
32 | this, your pull request's comments will ideally include a test plan that the | ||
33 | reviewers can use to (1) demonstrate the problem on master, if applicable and | ||
34 | (2) verify that the problem no longer exists with your changes applied (or that | ||
35 | your new features work correctly). Document all of the edge cases you're aware | ||
36 | of so we can adequately test them - then verify the test plan yourself before | ||
37 | submitting. | ||
47 | 38 | ||
48 | ## Commit Messages | 39 | ## Commit Messages |
49 | 40 | ||
50 | Please strive to write good commit messages. Here's some guidelines to follow: | 41 | Please strive to write good commit messages. Here's some guidelines to follow: |
51 | 42 | ||
52 | The first line should be limited to 50 characters and should be a sentence that | 43 | The first line should be limited to 50 characters and should be a sentence that |
53 | completes the thought [When applied, this commit will...] "Implement cmd_move" | 44 | completes the thought [When applied, this commit will...] *"Implement |
54 | or "Fix #742" or "Improve performance of arrange_windows on ARM" or similar. | 45 | cmd_move"* or *"Fix #742"* or *"Improve performance of arrange_windows on ARM"* |
46 | or similar. | ||
55 | 47 | ||
56 | The subsequent lines should be seperated from the subject line by a single | 48 | The subsequent lines should be separated from the subject line by a single |
57 | blank line, and include optional details. In this you can give justification | 49 | blank line, and include optional details. In this you can give justification |
58 | for the change, [reference Github | 50 | for the change, [reference Github |
59 | issues](https://help.github.com/articles/closing-issues-via-commit-messages/), | 51 | issues](https://help.github.com/articles/closing-issues-via-commit-messages/), |
60 | or explain some of the subtler details of your patch. This is important because | 52 | or explain some of the subtler details of your patch. This is important because |
61 | when someone finds a line of code they don't understand later, they can use the | 53 | when someone finds a line of code they don't understand later, they can use the |
62 | `git blame` command to find out what the author was thinking when they wrote | 54 | `git blame` command to find out what the author was thinking when they wrote |
63 | it. It's also easier to review your pull requests if they're seperated into | 55 | it. It's also easier to review your pull requests if they're separated into |
64 | logical commits that have good commit messages and justify themselves in the | 56 | logical commits that have good commit messages and justify themselves in the |
65 | extended commit description. | 57 | extended commit description. |
66 | 58 | ||
@@ -68,157 +60,178 @@ As a good rule of thumb, anything you might put into the pull request | |||
68 | description on Github is probably fair game for going into the extended commit | 60 | description on Github is probably fair game for going into the extended commit |
69 | message as well. | 61 | message as well. |
70 | 62 | ||
71 | ## Coding Style | 63 | See [here](https://chris.beams.io/posts/git-commit/) for more details. |
72 | 64 | ||
73 | Sway is written in C. The style guidelines is [kernel | 65 | ## Code Review |
74 | style](https://www.kernel.org/doc/Documentation/process/coding-style.rst), but all braces go | 66 | |
75 | on the same line (*"but K&R says so!" is a silly way of justifying something*). | 67 | When your changes are submitted for review, one or more core committers will |
76 | Some points to note: | 68 | look over them. Smaller changes might be merged with little fanfare, but larger |
77 | 69 | changes will typically see review from several people. Be prepared to receive | |
78 | * Do not use typedefs unless you have a good reason | 70 | some feedback - you may be asked to make changes to your work. Our code review |
79 | * Do not use macros unless you have a *really* good reason | 71 | process is: |
80 | * Align `case` with `switch` | 72 | |
81 | * Tabs, not spaces | 73 | 1. **Triage** the pull request. Do the commit messages make sense? Is a test |
82 | * `char *pointer` - note position of `*` | 74 | plan necessary and/or present? Add anyone as reviewers that you think should |
83 | * Use logging with reckless abandon | 75 | be there (using the relevant GitHub feature, if you have the permissions, or |
84 | * Always include braces for if/for/while/etc, even for one-liners | 76 | with an @mention if necessary). |
85 | 77 | 2. **Review** the code. Look for code style violations, naming convention | |
86 | An example of well formatted code: | 78 | violations, buffer overflows, memory leaks, logic errors, non-portable code |
87 | 79 | (including GNU-isms), etc. For significant changes to the public API, loop in | |
88 | ```C | 80 | a couple more people for discussion. |
89 | #include <stdio.h> | 81 | 3. **Execute** the test plan, if present. |
90 | #include <stdlib.h> | 82 | 4. **Merge** the pull request when all reviewers approve. |
91 | #include "log.h" | 83 | 5. **File** follow-up tickets if appropriate. |
92 | #include "example.h" | 84 | |
93 | 85 | ## Style Reference | |
94 | struct foobar { | 86 | |
95 | char *foo; | 87 | Sway is written in C with a style similar to the [kernel |
96 | int bar; | 88 | style](https://www.kernel.org/doc/Documentation/process/coding-style.rst), but |
97 | long baz; | 89 | with a few notable differences. |
98 | }; // Do not typedef without a good reason | 90 | |
99 | 91 | Try to keep your code conforming to C11 and POSIX as much as possible, and do | |
100 | int main(int argc, const char **argv) { | 92 | not use GNU extensions. |
101 | if (argc != 4) { | 93 | |
102 | sway_abort("Do not run this program manually. See man 5 sway and look for output options."); | 94 | ### Brackets |
95 | |||
96 | Brackets always go on the same line, including in functions. | ||
97 | Always include brackets for if/while/for, even if it's a single statement. | ||
98 | ```c | ||
99 | void function(void) { | ||
100 | if (condition1) { | ||
101 | do_thing1(); | ||
103 | } | 102 | } |
104 | 103 | ||
105 | if (!registry->desktop_shell) { | 104 | if (condition2) { |
106 | sway_abort("swaybg requires the compositor to support the desktop-shell extension."); | 105 | do_thing2(); |
106 | } else { | ||
107 | do_thing3(); | ||
107 | } | 108 | } |
109 | } | ||
110 | ``` | ||
111 | |||
112 | ### Indentation | ||
113 | |||
114 | Indentations are a single tab. | ||
115 | |||
116 | For long lines that need to be broken, the continuation line should be indented | ||
117 | with an additional tab. | ||
118 | |||
119 | If the line being broken is opening a new block (functions, if, while, etc.), | ||
120 | the continuation line should be indented with two tabs, so they can't be | ||
121 | misread as being part of the block. | ||
122 | |||
123 | ```c | ||
124 | really_long_function(argument1, argument2, ..., | ||
125 | argument3, argument4); | ||
126 | |||
127 | if (condition1 && condition2 && ... | ||
128 | condition3 && condition4) { | ||
129 | do_thing(); | ||
130 | } | ||
131 | ``` | ||
132 | |||
133 | Try to break the line in the place which you think is the most appropriate to | ||
134 | balance the lines. | ||
135 | |||
136 | ### Line Length | ||
137 | |||
138 | Try to keep your lines under 80 columns, but you can go up to 100 if it | ||
139 | improves readability. Don't break lines indiscriminately, try to find nice | ||
140 | breaking points so your code is easy to read. | ||
141 | |||
142 | ### Names | ||
143 | |||
144 | Global function and type names should be prefixed with `sway_submodule_` (e.g. | ||
145 | `struct sway_output`, `sway_output_destroy`). For static functions and | ||
146 | types local to a file, the names chosen aren't as important. Static functions | ||
147 | shouldn't have a `sway_` prefix. | ||
148 | |||
149 | For include guards, use the header's filename relative to include. Uppercase | ||
150 | all of the characters, and replace any invalid characters with an underscore. | ||
108 | 151 | ||
109 | int desired_output = atoi(argv[1]); | 152 | ### Construction/Destruction Functions |
110 | sway_log(WLR_INFO, "Using output %d of %d", desired_output, registry->outputs->length); | 153 | |
111 | int i; | 154 | For functions that are responsible for constructing and destructing an object, |
112 | struct output_state *output = registry->outputs->items[desired_output]; | 155 | they should be written as a pair of one of two forms: |
113 | struct window *window = window_setup(registry, 100, 100, false); | 156 | |
114 | if (!window) { | 157 | * `init`/`finish`: These initialize/deinitialize a type, but are **NOT** |
115 | sway_abort("Failed to create surfaces."); | 158 | responsible for allocating it. They should accept a pointer to some |
159 | pre-allocated memory (e.g. a member of a struct). | ||
160 | * `create`/`destroy`: These also initialize/deinitialize, but will return a | ||
161 | pointer to a `malloc`ed chunk of memory, and will `free` it in `destroy`. | ||
162 | |||
163 | A destruction function should always be able to accept a NULL pointer or a | ||
164 | zeroed value and exit cleanly; this simplifies error handling a lot. | ||
165 | |||
166 | ### Error Codes | ||
167 | |||
168 | For functions not returning a value, they should return a (stdbool.h) bool to | ||
169 | indicated if they succeeded or not. | ||
170 | |||
171 | ### Macros | ||
172 | |||
173 | Keep the use of macros to a minimum, especially if a function can do the job. If | ||
174 | you do need to use them, try to keep them close to where they're being used and | ||
175 | `#undef` them after. | ||
176 | |||
177 | ### Example | ||
178 | |||
179 | ```c | ||
180 | struct wlr_backend *wlr_backend_autocreate(struct wl_display *display) { | ||
181 | struct wlr_backend *backend; | ||
182 | if (getenv("WAYLAND_DISPLAY") || getenv("_WAYLAND_DISPLAY")) { | ||
183 | backend = attempt_wl_backend(display); | ||
184 | if (backend) { | ||
185 | return backend; | ||
186 | } | ||
116 | } | 187 | } |
117 | window->width = output->width; | 188 | |
118 | window->height = output->height; | 189 | const char *x11_display = getenv("DISPLAY"); |
119 | desktop_shell_set_background(registry->desktop_shell, output->output, window->surface); | 190 | if (x11_display) { |
120 | list_add(surfaces, window); | 191 | return wlr_x11_backend_create(display, x11_display); |
121 | |||
122 | cairo_surface_t *image = cairo_image_surface_create_from_png(argv[2]); | ||
123 | double width = cairo_image_surface_get_width(image); | ||
124 | double height = cairo_image_surface_get_height(image); | ||
125 | |||
126 | const char *scaling_mode_str = argv[3]; | ||
127 | enum scaling_mode scaling_mode; | ||
128 | if (strcmp(scaling_mode_str, "stretch") == 0) { | ||
129 | scaling_mode = SCALING_MODE_STRETCH; | ||
130 | } else if (strcmp(scaling_mode_str, "fill") == 0) { | ||
131 | scaling_mode = SCALING_MODE_FILL; | ||
132 | } else if (strcmp(scaling_mode_str, "fit") == 0) { | ||
133 | scaling_mode = SCALING_MODE_FIT; | ||
134 | } else if (strcmp(scaling_mode_str, "center") == 0) { | ||
135 | scaling_mode = SCALING_MODE_CENTER; | ||
136 | } else if (strcmp(scaling_mode_str, "tile") == 0) { | ||
137 | scaling_mode = SCALING_MODE_TILE; | ||
138 | } else { | ||
139 | sway_abort("Unsupported scaling mode: %s", scaling_mode_str); | ||
140 | } | 192 | } |
141 | 193 | ||
142 | for (i = 0; i < surfaces->length; ++i) { | 194 | // Attempt DRM+libinput |
143 | struct window *window = surfaces->items[i]; | 195 | |
144 | if (window_prerender(window) && window->cairo) { | 196 | struct wlr_session *session = wlr_session_create(display); |
145 | switch (scaling_mode) { | 197 | if (!session) { |
146 | case SCALING_MODE_STRETCH: | 198 | wlr_log(WLR_ERROR, "Failed to start a DRM session"); |
147 | cairo_scale(window->cairo, | 199 | return NULL; |
148 | (double) window->width / width, | 200 | } |
149 | (double) window->height / height); | 201 | |
150 | cairo_set_source_surface(window->cairo, image, 0, 0); | 202 | int gpu = wlr_session_find_gpu(session); |
151 | break; | 203 | if (gpu == -1) { |
152 | case SCALING_MODE_FILL: | 204 | wlr_log(WLR_ERROR, "Failed to open DRM device"); |
153 | { | 205 | goto error_session; |
154 | double window_ratio = (double) window->width / window->height; | ||
155 | double bg_ratio = width / height; | ||
156 | |||
157 | if (window_ratio > bg_ratio) { | ||
158 | double scale = (double) window->width / width; | ||
159 | cairo_scale(window->cairo, scale, scale); | ||
160 | cairo_set_source_surface(window->cairo, image, | ||
161 | 0, | ||
162 | (double) window->height/2 / scale - height/2); | ||
163 | } else { | ||
164 | double scale = (double) window->height / height; | ||
165 | cairo_scale(window->cairo, scale, scale); | ||
166 | cairo_set_source_surface(window->cairo, image, | ||
167 | (double) window->width/2 / scale - width/2, | ||
168 | 0); | ||
169 | } | ||
170 | break; | ||
171 | } | ||
172 | case SCALING_MODE_FIT: | ||
173 | { | ||
174 | double window_ratio = (double) window->width / window->height; | ||
175 | double bg_ratio = width / height; | ||
176 | |||
177 | if (window_ratio > bg_ratio) { | ||
178 | double scale = (double) window->height / height; | ||
179 | cairo_scale(window->cairo, scale, scale); | ||
180 | cairo_set_source_surface(window->cairo, image, | ||
181 | (double) window->width/2 / scale - width/2, | ||
182 | 0); | ||
183 | } else { | ||
184 | double scale = (double) window->width / width; | ||
185 | cairo_scale(window->cairo, scale, scale); | ||
186 | cairo_set_source_surface(window->cairo, image, | ||
187 | 0, | ||
188 | (double) window->height/2 / scale - height/2); | ||
189 | } | ||
190 | break; | ||
191 | } | ||
192 | case SCALING_MODE_CENTER: | ||
193 | cairo_set_source_surface(window->cairo, image, | ||
194 | (double) window->width/2 - width/2, | ||
195 | (double) window->height/2 - height/2); | ||
196 | break; | ||
197 | case SCALING_MODE_TILE: | ||
198 | { | ||
199 | cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image); | ||
200 | cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); | ||
201 | cairo_set_source(window->cairo, pattern); | ||
202 | break; | ||
203 | } | ||
204 | default: | ||
205 | sway_abort("Scaling mode '%s' not implemented yet!", scaling_mode_str); | ||
206 | } | ||
207 | |||
208 | cairo_paint(window->cairo); | ||
209 | |||
210 | window_render(window); | ||
211 | } | ||
212 | } | 206 | } |
213 | 207 | ||
214 | while (wl_display_dispatch(registry->display) != -1); | 208 | backend = wlr_multi_backend_create(session); |
209 | if (!backend) { | ||
210 | goto error_gpu; | ||
211 | } | ||
215 | 212 | ||
216 | for (i = 0; i < surfaces->length; ++i) { | 213 | struct wlr_backend *libinput = wlr_libinput_backend_create(display, session); |
217 | struct window *window = surfaces->items[i]; | 214 | if (!libinput) { |
218 | window_teardown(window); | 215 | goto error_multi; |
219 | } | 216 | } |
220 | list_free(surfaces); | 217 | |
221 | registry_teardown(registry); | 218 | struct wlr_backend *drm = wlr_drm_backend_create(display, session, gpu); |
222 | return 0; | 219 | if (!drm) { |
220 | goto error_libinput; | ||
221 | } | ||
222 | |||
223 | wlr_multi_backend_add(backend, libinput); | ||
224 | wlr_multi_backend_add(backend, drm); | ||
225 | return backend; | ||
226 | |||
227 | error_libinput: | ||
228 | wlr_backend_destroy(libinput); | ||
229 | error_multi: | ||
230 | wlr_backend_destroy(backend); | ||
231 | error_gpu: | ||
232 | wlr_session_close_file(session, gpu); | ||
233 | error_session: | ||
234 | wlr_session_destroy(session); | ||
235 | return NULL; | ||
223 | } | 236 | } |
224 | ``` | 237 | ``` |