diff --git a/buffyboard/buffyboard.h b/buffyboard/buffyboard.h
index 85ac9e5bc7733e83ac45c0065a930ad0441140a0..1d9ad09f479d39513b4e7f594176afe0b14f8ec5 100644
--- a/buffyboard/buffyboard.h
+++ b/buffyboard/buffyboard.h
@@ -9,8 +9,8 @@
 
 #include "lvgl/lvgl.h"
 
-#ifndef BB_VERSION
-#define BB_VERSION "?" /* Just to silence IDE warning. Real version injected by meson during build. */
+#ifndef PROJECT_VERSION
+#define PROJECT_VERSION "?" /* Just to silence IDE warning. Real version injected by meson during build. */
 #endif
 
 #endif /* BB_BUFFYBOARD_H */
diff --git a/buffyboard/command_line.c b/buffyboard/command_line.c
index 8f51ffdb73cf1ebcb540650c923e618254defa94..a357c285215dab73ccb32c8d3981150740281239 100644
--- a/buffyboard/command_line.c
+++ b/buffyboard/command_line.c
@@ -154,7 +154,7 @@ void bb_cli_parse_opts(int argc, char *argv[], bb_cli_opts *opts) {
             opts->verbose = true;
             break;
         case 'V':
-            fprintf(stderr, "buffyboard %s\n", BB_VERSION);
+            fprintf(stderr, "buffyboard %s\n", PROJECT_VERSION);
             exit(0);
         default:
             print_usage();
diff --git a/buffyboard/meson.build b/buffyboard/meson.build
index fef9943de75ddfd9602a3f696455671d75963621..b80c42fe20fef82b27be3a28c1cbe59d32ba0d95 100644
--- a/buffyboard/meson.build
+++ b/buffyboard/meson.build
@@ -1,68 +1,26 @@
 # Copyright 2021 Johannes Marbach
 # SPDX-License-Identifier: GPL-3.0-or-later
 
+buffyboard_sources = files(
+    'command_line.c',
+    'config.c',
+    'main.c',
+    'sq2lv_layouts.c',
+    'terminal.c',
+    'uinput_device.c'
+)
 
-buffyboard_sources = [
-  'command_line.c',
-  'config.c',
-  'main.c',
-  'sq2lv_layouts.c',
-  'terminal.c',
-  'uinput_device.c',
-]
-
-shared_sources = [
-  '../shared/cursor/cursor.c',
-  '../shared/fonts/font_32.c',
-  '../shared/config.c',
-  '../shared/indev.c',
-  '../shared/log.c',
-  '../shared/theme.c',
-  '../shared/themes.c',
-]
-
-squeek2lvgl_sources = [
-  '../squeek2lvgl/sq2lv.c',
-]
-
-man_files = [
-	'doc/buffyboard.1',
-	'doc/buffyboard.conf.5',
+buffyboard_dependencies = [
+    common_dependencies,
+    meson.get_compiler('c').find_library('m', required: false)
 ]
 
-lvgl_sources = run_command('../find-lvgl-sources.sh', '../lvgl', check: true).stdout().strip().split('\n')
-
-install_data(sources: 'buffyboard.conf', install_dir : get_option('sysconfdir'))
-
-executable(
-  'buffyboard',
-  sources: buffyboard_sources + shared_sources + squeek2lvgl_sources + lvgl_sources,
-  include_directories: ['..'],
-  dependencies: [
-    dependency('inih'),
-    dependency('libinput'),
-    dependency('libudev'),
-    meson.get_compiler('c').find_library('m', required: false),
-  ],
-  install: true
+executable('buffyboard',
+    include_directories: common_include_dirs,
+    sources: buffyboard_sources + shared_sources + squeek2lvgl_sources + lvgl_sources,
+    dependencies: buffyboard_dependencies,
+    install: true
 )
 
-scdoc = dependency('scdoc')
-scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native : true)
-sh = find_program('sh', native : true)
-foreach file : man_files
-	filename = file + '.scd'
-	section = file.split('.')[-1]
-	topic = file.split('.' + section)[-2].split('/')[-1]
-	output = '@0@.@1@'.format(topic, section)
+install_data('buffyboard.conf', install_dir: get_option('sysconfdir'))
 
-	custom_target(
-		output,
-		input : filename,
-		output : output,
-		capture : true,
-		command : [sh, '-c', scdoc_prog.path() + ' < @INPUT@'],
-		install : true,
-		install_dir : get_option('mandir') / 'man' + section
-	)
-endforeach
diff --git a/buffyboard/doc/buffyboard.1.scd b/man/buffyboard.1.scd
similarity index 100%
rename from buffyboard/doc/buffyboard.1.scd
rename to man/buffyboard.1.scd
diff --git a/buffyboard/doc/buffyboard.conf.5.scd b/man/buffyboard.conf.5.scd
similarity index 100%
rename from buffyboard/doc/buffyboard.conf.5.scd
rename to man/buffyboard.conf.5.scd
diff --git a/man/meson.build b/man/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..b1ff61fcad55fdb5a33ec5e3987a11852ddb3e26
--- /dev/null
+++ b/man/meson.build
@@ -0,0 +1,21 @@
+progscdoc = depscdoc.get_variable(pkgconfig: 'scdoc')
+
+foreach file : [
+        'buffyboard.1',
+        'buffyboard.conf.5',
+        'unl0kr.1',
+        'unl0kr.conf.5'
+    ]
+
+    section = file.split('.')[-1]
+
+    custom_target(file,
+        command: progscdoc,
+        feed: true,
+        capture: true,
+        input: file + '.scd',
+        output: file,
+        install: true,
+        install_dir: get_option('mandir') / 'man' + section
+    )
+endforeach
diff --git a/unl0kr/doc/unl0kr.1.scd b/man/unl0kr.1.scd
similarity index 100%
rename from unl0kr/doc/unl0kr.1.scd
rename to man/unl0kr.1.scd
diff --git a/unl0kr/doc/unl0kr.conf.5.scd b/man/unl0kr.conf.5.scd
similarity index 100%
rename from unl0kr/doc/unl0kr.conf.5.scd
rename to man/unl0kr.conf.5.scd
diff --git a/meson.build b/meson.build
index 4c0f3a702ebae0413ba32e4bb6bdf4d5060b9f1c..0b638d4a6b5794ac1810b471a2b49c90964da23b 100644
--- a/meson.build
+++ b/meson.build
@@ -1,13 +1,48 @@
-project(
-  'buffybox',
-  'c',
-  version: '3.2.0',
-  default_options: 'warning_level=3',
-  meson_version: '>=0.53.0'
+project('buffybox', 'c',
+    version: '3.2.0',
+    default_options: 'warning_level=3',
+    meson_version: '>= 0.59.0'
 )
 
-add_project_arguments('-DBB_VERSION="@0@"'.format(meson.project_version()), language: ['c'])
-add_project_arguments('-DUL_VERSION="@0@"'.format(meson.project_version()), language: ['c'])
+add_project_arguments('-DPROJECT_VERSION="@0@"'.format(meson.project_version()), language: 'c')
+
+depinih      = dependency('inih')
+deplibinput  = dependency('libinput')
+deplibudev   = dependency('libudev')
+
+if get_option('man')
+    depscdoc = dependency('scdoc')
+endif
+
+common_include_dirs = include_directories('.')
+
+shared_sources = files(
+    'shared/cursor/cursor.c',
+    'shared/fonts/font_32.c',
+    'shared/config.c',
+    'shared/indev.c',
+    'shared/log.c',
+    'shared/theme.c',
+    'shared/themes.c'
+)
+
+squeek2lvgl_sources = files(
+    'squeek2lvgl/sq2lv.c'
+)
+
+lvgl_sources = files(
+    run_command('find-lvgl-sources.sh', 'lvgl', check: true).stdout().strip().split('\n')
+)
+
+common_dependencies = [
+    depinih,
+    deplibinput,
+    deplibudev
+]
 
 subdir('unl0kr')
 subdir('buffyboard')
+
+if get_option('man')
+    subdir('man')
+endif
diff --git a/meson_options.txt b/meson_options.txt
index 2b2783b1e44e95ba99d08c1479a87b4b40fd928d..23c654783d606d3fb71d87f15f2d408002c1f905 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -1 +1,2 @@
-option('with-drm', type : 'feature', value : 'auto', description : 'Enable DRM backend')
+option('with-drm', type: 'feature', value: 'auto', description: 'Enable DRM backend')
+option('man', type: 'boolean', value: true, description: 'Install manual pages')
diff --git a/unl0kr/command_line.c b/unl0kr/command_line.c
index df8e6f69a25ac2c044573795ab12d1422f27c7a0..1f245c6f689968ad355203cd2da70006099d2a82 100644
--- a/unl0kr/command_line.c
+++ b/unl0kr/command_line.c
@@ -126,7 +126,7 @@ void ul_cli_parse_opts(int argc, char *argv[], ul_cli_opts *opts) {
             opts->verbose = true;
             break;
         case 'V':
-            fprintf(stderr, "unl0kr %s\n", UL_VERSION);
+            fprintf(stderr, "unl0kr %s\n", PROJECT_VERSION);
             exit(0);
         default:
             print_usage();
diff --git a/unl0kr/main.c b/unl0kr/main.c
index 2742ef0f1ad20bbd0f6908915f3a0160b3ec3b72..e711e44e712e24a1786647e92179ab9ac81cae9c 100644
--- a/unl0kr/main.c
+++ b/unl0kr/main.c
@@ -377,7 +377,7 @@ int main(int argc, char *argv[]) {
     }
 
     /* Announce ourselves */
-    bbx_log(BBX_LOG_LEVEL_VERBOSE, "unl0kr %s", UL_VERSION);
+    bbx_log(BBX_LOG_LEVEL_VERBOSE, "unl0kr %s", PROJECT_VERSION);
 
     /* Parse config files */
     ul_config_init_opts(&conf_opts);
diff --git a/unl0kr/meson.build b/unl0kr/meson.build
index ffc2727539d5acefaeae23c02f6a2894de77bf2e..076ce0b8f229c81e19612fa0df68e4086a729722 100644
--- a/unl0kr/meson.build
+++ b/unl0kr/meson.build
@@ -1,76 +1,34 @@
 # Copyright 2021 Clayton Craft
 # SPDX-License-Identifier: GPL-3.0-or-later
 
-
-unl0kr_sources = [
-  'backends.c',
-  'command_line.c',
-  'config.c',
-  'main.c',
-  'sq2lv_layouts.c',
-  'terminal.c',
-]
-
-shared_sources = [
-  '../shared/cursor/cursor.c',
-  '../shared/fonts/font_32.c',
-  '../shared/config.c',
-  '../shared/indev.c',
-  '../shared/log.c',
-  '../shared/theme.c',
-  '../shared/themes.c',
-]
-
-squeek2lvgl_sources = [
-  '../squeek2lvgl/sq2lv.c',
-]
-
-man_files = [
-	'doc/unl0kr.1',
-	'doc/unl0kr.conf.5',
-]
+depxkbcommon = dependency('xkbcommon')
+
+unl0kr_sources = files(
+    'backends.c',
+    'command_line.c',
+    'config.c',
+    'main.c',
+    'sq2lv_layouts.c',
+    'terminal.c'
+)
 
 unl0kr_dependencies = [
-  dependency('inih'),
-  dependency('libinput'),
-  dependency('libudev'),
-  dependency('xkbcommon'),
+    common_dependencies,
+    depxkbcommon
 ]
 
-libdrm_dep = dependency('libdrm', required: get_option('with-drm'))
-if libdrm_dep.found()
-  unl0kr_dependencies += [libdrm_dep]
-  add_project_arguments('-DLV_USE_LINUX_DRM=1', language: ['c'])
+deplibdrm = dependency('libdrm', required: get_option('with-drm'))
+if deplibdrm.found()
+    unl0kr_dependencies += deplibdrm
+    add_project_arguments('-DLV_USE_LINUX_DRM=1', language: 'c')
 endif
 
-lvgl_sources = run_command('../find-lvgl-sources.sh', '../lvgl', check: true).stdout().strip().split('\n')
-
-install_data(sources: 'unl0kr.conf', install_dir : get_option('sysconfdir'))
-
-executable(
-  'unl0kr',
-  sources: unl0kr_sources + shared_sources + squeek2lvgl_sources + lvgl_sources,
-  include_directories: ['..'],
-  dependencies: unl0kr_dependencies,
-  install: true
+executable('unl0kr',
+    include_directories: common_include_dirs,
+    sources: unl0kr_sources + shared_sources + squeek2lvgl_sources + lvgl_sources,
+    dependencies: unl0kr_dependencies,
+    install: true
 )
 
-scdoc = dependency('scdoc')
-scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native : true)
-sh = find_program('sh', native : true)
-foreach file : man_files
-	filename = file + '.scd'
-	section = file.split('.')[-1]
-	topic = file.split('.' + section)[-2].split('/')[-1]
-	output = '@0@.@1@'.format(topic, section)
+install_data('unl0kr.conf', install_dir: get_option('sysconfdir'))
 
-	custom_target(
-		output,
-		input : filename,
-		output : output,
-		capture : true,
-		command : [sh, '-c', scdoc_prog.path() + ' < @INPUT@'],
-		install : true,
-		install_dir : get_option('mandir') / 'man' + section
-	)
-endforeach
diff --git a/unl0kr/unl0kr.h b/unl0kr/unl0kr.h
index 7827314ca25c01a5f7f0e09bed0e2f7fa675a4d7..ced14d68f43e700b4bae93d8a1023f8e29d773a5 100644
--- a/unl0kr/unl0kr.h
+++ b/unl0kr/unl0kr.h
@@ -9,8 +9,8 @@
 
 #include "lvgl/lvgl.h"
 
-#ifndef UL_VERSION
-#define UL_VERSION "?" /* Just to silence IDE warning. Real version injected by meson during build. */
+#ifndef PROJECT_VERSION
+#define PROJECT_VERSION "?" /* Just to silence IDE warning. Real version injected by meson during build. */
 #endif
 
 /**