SVNからのミラー

This commit is contained in:
2026-01-21 03:15:16 +09:00
commit 9a3ed812ad
30 changed files with 9312 additions and 0 deletions

15
CHANGELOG.md Normal file
View File

@@ -0,0 +1,15 @@
# 1.0.1 (2025/02/11)
* XenoWMでタイルと中央モードに設定する場合、色の修正
# 1.0.0 (2025/02/10)
* C言語の「uw」とC++の「uw-setter」はつのC++の「uw」にマージ
* モードプルダウンの追加
* 起動時間が10万%速めた
* 画像を選択したら、枠を見える様に
* 全画面モード「-f」の修正
* マニュアルページの追加
* 中央モード「-c」の修正
* 「背景の設置」ボタンの修正
# 0.1.0 (2024/09/16)
* 開始

13
LICENSE.txt Normal file
View File

@@ -0,0 +1,13 @@
Copyright © 2018-2025 by 076.moe
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.

122
Makefile Normal file
View File

@@ -0,0 +1,122 @@
UNAME_S != uname -s
UNAME_M != uname -m
OS = ${UNAME_S}
ARCH = ${UNAME_M}
.if ${UNAME_S} == "OpenBSD"
OS = openbsd
.elif ${UNAME_S} == "NetBSD"
OS = netbsd
.elif ${UNAME_S} == "FreeBSD"
OS = freebsd
.elif ${UNAME_S} == "Dragonfly"
OS = dragonfly
.elif ${UNAME_S} == "Linux"
OS = linux
.endif
.if ${UNAME_M} == "x86_64"
ARCH = amd64
.endif
NAME != cat src/fe/window.cc | grep "name =" | awk '{print $$3}' |\
sed "s/\"//g" | sed "s/;//"
VERSION != cat src/fe/window.cc | grep "version =" | awk '{print $$3}' |\
sed "s/\"//g" | sed "s/;//"
PREFIX = /usr/local
.if ${OS} == "linux"
PREFIX = /usr
.endif
CC = c++
FILES = main.cc src/uw/*.cc src/fe/*.cc
.if ${OS} == "openbsd"
DEPS = pkg_add fltk jpeg png
.elif ${OS} == "netbsd"
DEPS = pkgin install fltk libjpeg-turbo png
.elif ${OS} == "freebsd"
DEPS = pkg install fltk libjpeg-turbo png xorg xcb
.endif
CFLAGS = -std=c++20\
-Wall -Wextra -pthread -Wno-non-c-typedef-for-linkage -I/usr/include\
-L/usr/lib
.if ${OS} == "freebsd" || ${OS} == "openbsd" || ${OS} == "netbsd" || ${OS} == "dragonfly
CFLAGS += -I/usr/local/include -L/usr/local/lib
.endif
.if ${OS} == "netbsd"
CFLAGS += -I/usr/X11R7/include -L/usr/X11R7/lib -I/usr/pkg/include -L/usr/pkg/lib
.elif ${OS} == "openbsd"
CFLAGS += -I/usr/X11R6/include -L/usr/X11R6/lib
.endif
LDFLAGS = -lfltk -lfltk_images -lX11
.if ${OS} == "netbsd"
LDFLAGS += -lpng16 -ljpeg
.endif
SLIB = -lc++
.if ${OS} == "openbsd"
SLIB = -lc++abi -lpthread -lm -lc \
-lXcursor -lXfixes -lXext -lXft -lfontconfig -lXinerama -lXdmcp -lXau \
-lpng -lz -ljpeg -lxcb -lXrender -lexpat -lfreetype
.elif ${OS} == "freebsd"
SLIB = -lcxxrt -lm -lgcc -lXrender -lXcursor -lXfixes -lXext -lXft -lfontconfig\
-lXinerama -lthr -lpng16 -lz -ljpeg -lxcb -lfreetype -lexpat -lXau -lXdmcp\
-lbz2 -lbrotlidec -lbrotlicommon
.elif ${OS} == "netbsd"
SLIB = -lstdc++ -lpthread -lm -lc -lXft -lxcb -lfontconfig -lfreetype\
-lXau -lXdmcp -lXcursor -lXrandr -lXext -lXrender -lXfixes -lXinerama -lX11\
-lexpat -lz -lbz2 -lgcc
.elif ${OS} == "linux"
SLIB = -lstdc++ -lgcc -lc -lXft -lXext -lXrender -lfontconfig -lXinerama\
-lpng16 -lz -ljpeg -lxcb -lfreetype -lexpat -lXau -lXdmcp -lbz2\
-lbrotlidec -lbrotlicommon
.endif
all:
${CC} -O3 ${CFLAGS} -o ${NAME}\
${FILES} -static ${LDFLAGS} ${SLIB}
strip ${NAME}
depend:
${DEPS}
wrong:
${CC} -O3 ${CFLAGS} -o ${NAME}\
${FILES} ${LDFLAGS} ${SLIB}
strip ${NAME}
debug:
${CC} -g ${CFLAGS} -o ${NAME} ${FILES} ${LDFLAGS}
clean:
rm -rf ${NAME}
dist:
mkdir -p ${NAME}-${VERSION} release/src
cp -R LICENSE.txt Makefile README.md CHANGELOG.md\
main.cc ${NAME}-${VERSION}
tar zcfv release/src/${NAME}-${VERSION}.tar.gz ${NAME}-${VERSION}
rm -rf ${NAME}-${VERSION}
release:
mkdir -p release/bin/${VERSION}/${OS}/${ARCH} release/man/${VERSION}
${CC} -O3 ${CFLAGS} -o release/bin/${VERSION}/${OS}/${ARCH}/${NAME} ${FILES}\
-static ${LDFLAGS} ${SLIB}
strip release/bin/${VERSION}/${OS}/${ARCH}/${NAME}
cp ${NAME}.1 release/man/${VERSION}
install:
mkdir -p ${DESTDIR}${PREFIX}/bin ${DESTDIR}${PREFIX}/share/man/man1
cp -f ${NAME} ${DESTDIR}${PREFIX}/bin
chmod 755 ${DESTDIR}${PREFIX}/bin/${NAME}
cp -f ${NAME}.1 ${DESTDIR}${PREFIX}/share/man/man1
uninstall:
rm -rf ${DESTDIR}${PREFIX}/bin/${NAME}
rm -rf${DESTDIR}${PREFIX}/share/man/man1/${NAME}.1
.PHONY: all depend wrong debug clean dist release install uninstall

71
README.md Normal file
View File

@@ -0,0 +1,71 @@
# uw
只の背景変更ツール。
XorgとFLTK以外、従属ライブラリを使いません。
## 従属ソフト | Dependensy
### OpenBSD
```sh
doas make deps
```
### NetBSD
```sh
doas make deps
```
### FreeBSD
```sh
doas make deps
```
### Void Linux
```sh
doas xbps-install -S fltk fltk-devel libX11 libX11-devel xcb-util xcb-util-devel libpng libpng-devel libjpeg-turbo libjpeg-turbo-devel
```
## 使い方 | Usage
### GUIの起動
```sh
uw
```
### CLIで背景の変更
#### タイル
```sh
uw -t wallpaper.png
```
#### 中央
```sh
uw -c wallpaper.png
```
#### 全画面
```sh
uw -f wallpaper.png
```
## インストールする方法 | Installation
### BSD
```sh
doas make depends
make
doas make install
```
### Void Linux
```sh
sudo xbps-install fltk fltk-devel libjpeg-turbo libjpeg-turbo-devel libpng libpng-devel
bmake
sudo bmake install
```
### Alpine Linux
```sh
sudo apk add fltk fltk-dev libjpeg-turbo libjpeg-turbo-dev libpng libpng-dev
bmake
sudo bmake install
```
![](scrot.png)

124
main.cc Normal file
View File

@@ -0,0 +1,124 @@
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Choice.H>
#include <FL/Fl_File_Chooser.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Scroll.H>
#include <FL/Fl_Image.H>
#include <FL/Fl_PNG_Image.H>
#include <FL/Fl_JPEG_Image.H>
#include <exception>
#include <iostream>
#include <string>
#include <sys/stat.h>
#include <dirent.h>
#include "src/fe/theme.hh"
#include "src/fe/window.hh"
#include "src/fe/widget.hh"
#include "src/fe/background.hh"
#include "src/fe/thumbnail.hh"
#include "src/uw/background.hh"
void thumbnail_cb(Fl_Widget *widget, void *userdata) {
(void)widget;
const char *filepath = static_cast<const char *>(userdata);
if (filepath) {
uw::Background b(filepath);
b.setWallpaper(uw::Mode::TILE);
fe::Widget::getSetButton()->deactivate();
}
}
void usage(fe::Window w) {
std::cout << w.getName() << "-" << w.getVersion() << '\n';
std::cout << "usage: " << w.getName() << " " << w.getOptions() << " <image>"
<< std::endl;
}
int main(int argc, char **argv) {
fe::Window w;
if (argc == 1) {
std::string tit = w.getName() + " " + w.getVersion();
Fl_Window *window =
new Fl_Window(w.getWidth(), w.getHeight(), tit.c_str());
fe::Theme::SetDarkTheme();
// ボタン
fe::Widget::setChooseButton(new Fl_Button(10, 740, 200, 40, "画像を選択"));
fe::Widget::setSetButton(new Fl_Button(230, 740, 200, 40, "背景の設置"));
fe::Widget::getSetButton()->deactivate();
// モード選択
Fl_Choice *modeChoice = new Fl_Choice(450, 740, 200, 40, "");
modeChoice->add("タイル");
modeChoice->add("中央");
modeChoice->add("全画像");
modeChoice->value(0);
modeChoice->align(FL_ALIGN_TOP);
modeChoice->color(FL_BACKGROUND_COLOR);
modeChoice->textcolor(FL_FOREGROUND_COLOR);
modeChoice->selection_color(FL_BACKGROUND2_COLOR);
fe::Widget::setModeChoice(modeChoice);
// スクロールウィンドウ
fe::Widget::setScroll(new Fl_Scroll(10, 10, 780, 720));
fe::Widget::getScroll()->type(Fl_Scroll::VERTICAL);
fe::Widget::getScroll()->begin();
// サムネール
fe::Thumbnail thumbnail;
thumbnail.init();
fe::Widget::getScroll()->end();
// ボタンで選択
fe::Widget::getChooseButton()->callback(fe::Background::chooseImageCb,
(void *)fe::Widget::getSetButton());
fe::Widget::getSetButton()->callback(fe::Background::setWallpaperCb);
fe::Widget::getScroll()->end();
window->end();
window->show(argc, argv);
return Fl::run();
}
if (argc < 3) {
usage(w);
return 1;
}
std::string modeStr = argv[1];
std::string filename = argv[2];
uw::Mode mode;
if (modeStr == "-t") mode = uw::TILE;
else if (modeStr == "-f") mode = uw::FILL;
else if (modeStr == "-c") mode = uw::CENTER;
else {
usage(w);
return 1;
}
try {
uw::Background background(filename);
if (!background.setWallpaper(mode)) {
std::cerr << "背景を設定に失敗。" << std::endl;
return 1;
}
} catch (const std::exception &e) {
std::cerr << "エラー: " << e.what() << std::endl;
return 1;
}
return 0;
}

BIN
scrot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

7988
src/dep/stb_image.hh Normal file

File diff suppressed because it is too large Load Diff

60
src/fe/background.cc Normal file
View File

@@ -0,0 +1,60 @@
#include <FL/Fl_File_Chooser.H>
#include <cstdlib>
#include "background.hh"
#include "misc.hh"
#include "widget.hh"
std::string fe::Background::sfp;
namespace fe {
Background::Background() {}
Background::~Background() {}
uw::Mode Background::getMode() {
Fl_Choice *choice = Widget::getModeChoice();
if (!choice) return uw::Mode::TILE;
switch (choice->value()) {
case 0: return uw::Mode::TILE;
case 1: return uw::Mode::CENTER;
case 2: return uw::Mode::FILL;
default: return uw::Mode::TILE;
}
}
void Background::chooseImageCb(Fl_Widget *, void *) {
const char *homedir = std::getenv("HOME");
std::string defpath;
if (homedir) {
std::string userdir = std::string(homedir) + "/.local/share/wallpapers";
std::string localusr = "/usr/local/share/wallpapers";
std::string sysusr = "/usr/share/wallpapers";
if (Misc::isExist(userdir)) defpath = userdir;
else if (Misc::isExist(localusr)) defpath = localusr;
else defpath = sysusr;
}
Fl_File_Chooser chooser(defpath.c_str(), "*.{png,jpg}",
Fl_File_Chooser::SINGLE, "画像を御選択下さい・・・");
chooser.show();
while (chooser.shown()) Fl::wait();
if (chooser.value() != nullptr) {
setFilename(chooser.value());
Widget::getSetButton()->activate();
}
}
void Background::setWallpaperCb(Fl_Widget *, void *) {
if (!getFilename().empty()) {
uw::Background bg(getFilename());
bg.setWallpaper(getMode());
Widget::getSetButton()->deactivate();
}
}
}

27
src/fe/background.hh Normal file
View File

@@ -0,0 +1,27 @@
#pragma once
#include <FL/Fl_Widget.H>
#include <FL/Fl_Choice.H>
#include <string>
#include "../uw/background.hh"
namespace fe {
class Background {
public:
Background();
~Background();
static void chooseImageCb(Fl_Widget *, void *);
static void setWallpaperCb(Fl_Widget *, void *);
static void modeCHangeCb(Fl_Widget *, void *);
static std::string getFilename() { return sfp; }
static void setFilename(std::string filename) { sfp = filename; }
static uw::Mode getMode();
private:
static std::string sfp;
};
}

85
src/fe/clickablebox.cc Normal file
View File

@@ -0,0 +1,85 @@
#include <FL/fl_draw.H>
#include <fstream>
#include "clickablebox.hh"
#include "../fe/background.hh"
fe::ClickableBox *fe::ClickableBox::lastSelected = nullptr;
namespace fe {
int ClickableBox::handle(int event) {
switch (event) {
case FL_PUSH:
if (!imagePath.empty()) {
if (lastSelected && lastSelected != this) {
lastSelected->isSelected = false;
lastSelected->redraw();
}
isSelected = true;
lastSelected = this;
redraw();
background = std::make_unique<uw::Background>(imagePath);
background->setWallpaper(fe::Background().getMode());
alterXinitrc(getImagePath());
}
return 1;
default:
return Fl_Box::handle(event);
}
}
void ClickableBox::draw() {
Fl_Box::draw();
if (isSelected) {
fl_color(234, 121, 216);
fl_line_style(FL_SOLID, 9);
fl_rect(x(), y(), w(), h());
fl_line_style(0);
}
}
void ClickableBox::alterXinitrc(const std::string &npath) {
const std::string xinitrc = std::string(getenv("HOME")) + "/.xinitrc";
std::ifstream in(xinitrc);
std::vector<std::string> lines;
std::string line;
bool founduw = false;
bool addeduw = false;
std::string modeFlag;
switch(fe::Background().getMode()) {
case uw::Mode::TILE: modeFlag = "-t"; break;
case uw::Mode::CENTER: modeFlag = "-c"; break;
case uw::Mode::FILL: modeFlag = "-f"; break;
default: modeFlag = "-t"; break;
}
while (std::getline(in, line)) {
if (line.rfind("uw -", 0) == 0) {
line = "uw " + modeFlag + " " + npath + " &";
founduw = true;
}
lines.push_back(line);
if (line.rfind("exec ", 0) == 0 && !founduw && !addeduw) {
lines.insert(lines.end() - 1, "uw " + modeFlag + " " + npath + " &");
addeduw = true;
}
}
in.close();
if (!founduw && !addeduw) {
lines.push_back("uw " + modeFlag + " " + npath + " &");
}
std::ofstream out(xinitrc);
for (const auto &eline : lines) {
out << eline << std::endl;
}
out.close();
}
}

31
src/fe/clickablebox.hh Normal file
View File

@@ -0,0 +1,31 @@
#pragma once
#include <FL/Fl_Box.H>
#include <memory>
#include <string>
#include "../uw/background.hh"
namespace fe {
class ClickableBox : public Fl_Box {
public:
ClickableBox(int X, int Y, int W, int H, const char *L = 0)
: Fl_Box(X, Y, W, H, L), imagePath{ "" }, isSelected{ false } {}
void setImagePath(const std::string &path) { imagePath = path; }
std::string getImagePath() const { return imagePath; }
int handle(int event) override;
void draw() override;
private:
std::string imagePath;
std::unique_ptr<uw::Background> background;
bool isSelected;
static ClickableBox *lastSelected;
void alterXinitrc(const std::string &npath);
};
}

11
src/fe/misc.cc Normal file
View File

@@ -0,0 +1,11 @@
#include "misc.hh"
#include <filesystem>
#include <string>
namespace fe {
bool Misc::isExist(const std::string &path) {
std::filesystem::path fsPath(path);
return std::filesystem::exists(fsPath) && std::filesystem::is_directory(fsPath);
}
}

10
src/fe/misc.hh Normal file
View File

@@ -0,0 +1,10 @@
#pragma once
#include <string>
namespace fe {
class Misc {
public:
static bool isExist(const std::string &path);
};
}

11
src/fe/theme.cc Normal file
View File

@@ -0,0 +1,11 @@
#include <FL/Fl.H>
#include "theme.hh"
namespace fe {
void Theme::SetDarkTheme() {
Fl::background(35, 32, 35);
Fl::background2(68, 59, 68);
Fl::foreground(252, 252, 252);
}
}

10
src/fe/theme.hh Normal file
View File

@@ -0,0 +1,10 @@
#pragma once
namespace fe {
class Theme {
public:
static void SetDarkTheme();
private:
};
}

186
src/fe/thumbnail.cc Normal file
View File

@@ -0,0 +1,186 @@
#include <FL/Fl.H>
#include <FL/Fl_PNG_Image.H>
#include <FL/Fl_JPEG_Image.H>
#include <algorithm>
#include <cctype>
#include <filesystem>
#include <iostream>
#include <mutex>
#include "thumbnail.hh"
#include "clickablebox.hh"
#include "misc.hh"
#include "widget.hh"
namespace fe {
Thumbnail::Thumbnail() : loadingDialog(nullptr) {}
Thumbnail::~Thumbnail() {
for (auto img : thumbs) {
delete img;
}
if (loadingDialog) {
delete loadingDialog;
}
}
void Thumbnail::createLoadingDialog() {
Fl_Window *mainWindow = Widget::getScroll()->window();
if (!mainWindow) return;
int w = 200;
int h = 100;
int sw = mainWindow->w();
int sh = mainWindow->h();
int sx = mainWindow->x();
int sy = mainWindow->y();
int x = sx + (sw - w) / 2;
int y = sy + (sh - h) / 2;
loadingDialog = new Fl_Window(x, y, w, h, "読み込み中");
loadingDialog->set_modal();
Fl_Box *box =
new Fl_Box(0, 0, w, h, "画像を読み込んでいます。\nお待ち下さい。");
box->align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE);
loadingDialog->end();
loadingDialog->show();
loadingDialog->take_focus();
}
void Thumbnail::init() {
const char *home = getenv("HOME");
std::string homedir = "";
if (home) homedir = std::string(home) + "/.local/share/wallpapers";
std::string localdir = "/usr/local/share/wallpapers";
std::string sysdir = "/usr/share/wallpapers";
if (Misc::isExist(homedir)) {
std::vector<std::string> img = listImages(homedir);
imgfiles.insert(imgfiles.end(), img.begin(), img.end());
}
if (Misc::isExist(localdir)) {
std::vector<std::string> img = listImages(localdir);
imgfiles.insert(imgfiles.end(), img.begin(), img.end());
}
if (Misc::isExist(sysdir)) {
std::vector<std::string> img = listImages(sysdir);
imgfiles.insert(imgfiles.end(), img.begin(), img.end());
}
Widget::getScroll()->deactivate();
for (const auto &file : imgfiles) {
pending.push(file);
}
createLoadingDialog();
Fl::add_timeout(0.0, loadImagesCb, this);
}
void Thumbnail::loadImagesCb(void *v) {
Thumbnail *thumb = static_cast<Thumbnail *>(v);
thumb->loadNextImage();
}
void Thumbnail::loadNextImage() {
std::lock_guard<std::mutex> lock(mutex);
if (pending.empty()) {
if (loadingDialog) {
loadingDialog->hide();
delete loadingDialog;
loadingDialog = nullptr;
}
Widget::getScroll()->activate();
Widget::getScroll()->redraw();
return;
}
std::string filepath = pending.front();
pending.pop();
Fl_Image *img = nullptr;
std::string ext = filepath.substr(filepath.size() - 4);
if (ext == ".png") {
img = new Fl_PNG_Image(filepath.c_str());
} else if (ext == ".jpg") {
img = new Fl_JPEG_Image(filepath.c_str());
}
if (!img || img->w() <= 0 || img->h() <= 0) {
if (img) delete img;
if (!pending.empty()) Fl::add_timeout(0.0, loadImagesCb, this);
else {
if (loadingDialog) {
loadingDialog->hide();
delete loadingDialog;
loadingDialog = nullptr;
}
Widget::getScroll()->activate();
Widget::getScroll()->redraw();
}
return;
}
Fl_Image *thumb = img->copy(100, 100);
thumbs.push_back(thumb);
delete img;
ClickableBox *box = new ClickableBox(curX, curY, 100, 100);
box->image(thumb);
box->box(FL_FLAT_BOX);
box->setImagePath(filepath.c_str());
Widget::getScroll()->add(box);
Widget::getScroll()->redraw();
curX += 110;
if (curX + 110 > Widget::getScroll()->w()) {
curX = 10;
curY += 110;
}
if (!pending.empty()) Fl::add_timeout(0.01, loadImagesCb, this);
else {
if (loadingDialog) {
loadingDialog->hide();
delete loadingDialog;
loadingDialog = nullptr;
}
Widget::getScroll()->activate();
Widget::getScroll()->redraw();
}
}
std::vector<std::string> Thumbnail::listImages(const std::string &directory) {
std::vector<std::string> images;
std::filesystem::path dir(directory);
if (!std::filesystem::exists(dir) || !std::filesystem::is_directory(dir)) {
std::cerr << "ディレクトリを開くに失敗: " << directory << std::endl;
return images;
}
for (const auto &entry : std::filesystem::directory_iterator(dir)) {
if (!entry.is_regular_file()) continue;
std::string filename = entry.path().filename().string();
if (filename.size() <= 4) continue;
std::string ext = entry.path().extension().string();
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
if (ext == ".png" || ext == ".jpg") {
images.push_back(entry.path().string());
}
}
return images;
}
}

33
src/fe/thumbnail.hh Normal file
View File

@@ -0,0 +1,33 @@
#pragma once
#include <FL/Fl_Image.H>
#include <FL/Fl_Window.H>
#include <mutex>
#include <string>
#include <queue>
#include <vector>
namespace fe {
class Thumbnail {
public:
Thumbnail();
~Thumbnail();
void init();
static void loadImagesCb(void *v);
private:
std::vector<std::string> imgfiles;
std::vector<Fl_Image *> thumbs;
std::queue<std::string> pending;
std::mutex mutex;
int curX = 10;
int curY = 10;
Fl_Window *loadingDialog;
void createLoadingDialog();
std::vector<std::string> listImages(const std::string &directory);
void loadNextImage();
};
}

8
src/fe/widget.cc Normal file
View File

@@ -0,0 +1,8 @@
#include "widget.hh"
namespace fe {
Fl_Button *Widget::setButton = nullptr;
Fl_Button *Widget::chooseButton = nullptr;
Fl_Scroll *Widget::scroll = nullptr;
Fl_Choice *Widget::modeChoice = nullptr;
}

25
src/fe/widget.hh Normal file
View File

@@ -0,0 +1,25 @@
#pragma once
#include <FL/Fl_Button.H>
#include <FL/Fl_Scroll.H>
#include <FL/Fl_Choice.H>
namespace fe {
class Widget {
public:
static Fl_Button *getSetButton() { return setButton; }
static void setSetButton(Fl_Button *b) { setButton = b; }
static Fl_Button *getChooseButton() { return chooseButton; }
static void setChooseButton(Fl_Button *b) { chooseButton = b; }
static Fl_Scroll *getScroll() { return scroll; }
static void setScroll(Fl_Scroll *s) { scroll = s; }
static Fl_Choice *getModeChoice() { return modeChoice; }
static void setModeChoice(Fl_Choice *c) { modeChoice = c; }
private:
static Fl_Button *chooseButton;
static Fl_Button *setButton;
static Fl_Scroll *scroll;
static Fl_Choice *modeChoice;
};
}

13
src/fe/window.cc Normal file
View File

@@ -0,0 +1,13 @@
#include "window.hh"
namespace fe {
Window::Window() {
width = 800;
height = 800;
name = "uw";
version = "1.0.1";
avalopt = "[-cft]";
}
Window::~Window() {}
}

23
src/fe/window.hh Normal file
View File

@@ -0,0 +1,23 @@
#pragma once
#include <string>
namespace fe {
class Window {
public:
Window();
~Window();
int getWidth() { return width; }
int getHeight() { return height; }
std::string getName() { return name; }
std::string getVersion() { return version; }
std::string getOptions() { return avalopt; }
private:
int width;
int height;
std::string name;
std::string version;
std::string avalopt;
};
}

27
src/uw/background.cc Normal file
View File

@@ -0,0 +1,27 @@
#include <X11/Xlib.h>
#include "background.hh"
namespace uw {
Background::Background(const std::string &filename)
: filename(filename), i(s, filename), o(s, i) {
}
Background::~Background() {}
bool Background::setWallpaper(Mode mode) {
if (mode == Mode::FILL) {
o.fill();
} else if (mode == Mode::CENTER) {
o.center();
} else if (mode == Mode::TILE) {
o.tile();
} else {
return false;
}
XFlush(s.getDisplay());
return true;
}
}

29
src/uw/background.hh Normal file
View File

@@ -0,0 +1,29 @@
#pragma once
#include <string>
#include "screen.hh"
#include "orientation.hh"
#include "image.hh"
namespace uw {
enum Mode {
FILL,
CENTER,
TILE,
};
class Background {
public:
Background(const std::string &filepath);
~Background();
bool setWallpaper(Mode mode);
private:
std::string filename;
Screen s;
Image i;
Orientation o;
};
}

78
src/uw/image.cc Normal file
View File

@@ -0,0 +1,78 @@
#include <X11/Xlib.h>
#include <string>
#define STB_IMAGE_IMPLEMENTATION
#include "../dep/stb_image.hh"
#include <stdexcept>
#include "image.hh"
namespace uw {
Image::Image(Screen &screen, const std::string imgPath) : uwScreen{ screen } {
if (imgPath.empty()) throw std::runtime_error("画像パスが空です。");
data = stbi_load(imgPath.c_str(), &width, &height, &chan, 4);
if (!data) throw std::runtime_error("画像の読み込みに失敗: " + imgPath);
imageExtent.width = width;
imageExtent.height = height;
imagePath = imgPath;
v = DefaultVisual(screen.getDisplay(), screen.getScreen());
createDepth();
createPixmap();
createImage();
createGC();
}
Image::~Image() {
if (gc) XFreeGC(uwScreen.getDisplay(), gc);
if (pm) XFreePixmap(uwScreen.getDisplay(), pm);
if (image) XFree(image);
if (data) stbi_image_free(data);
}
void Image::createDepth() {
depth = DefaultDepth(uwScreen.getDisplay(), uwScreen.getScreen());
if (depth != 24 && depth != 32) {
throw std::runtime_error(
"" + std::to_string(depth) + "」の様な視覚の深さは未対応です。");
}
imageExtent.depth = depth;
}
void Image::createPixmap() {
pm = XCreatePixmap(uwScreen.getDisplay(), uwScreen.getWindow(), width, height,
depth);
}
void Image::createImage() {
if (depth == 24) {
std::vector<uchar> cd(data, data + (width * height * 4));
cdata = cd;
for (int i = 0; i < width * height; i++) {
cdata[i * 4 + 0] = data[i * 4 + 2];
cdata[i * 4 + 1] = data[i * 4 + 1];
cdata[i * 4 + 2] = data[i * 4 + 0];
cdata[i * 4 + 3] = data[i * 4 + 3];
}
image = XCreateImage(uwScreen.getDisplay(), v, depth, ZPixmap, 0,
(char *)cdata.data(), width, height, 32, 0);
} else {
image = XCreateImage(uwScreen.getDisplay(), v, depth, ZPixmap, 0,
(char *)data, width, height, 32, 0);
}
if (!image) throw std::runtime_error("XImageの作成に失敗。");
}
void Image::createGC() {
gc = XCreateGC(uwScreen.getDisplay(), pm, 0, nullptr);
if (!gc) throw std::runtime_error("GCの作成に失敗。");
}
}

55
src/uw/image.hh Normal file
View File

@@ -0,0 +1,55 @@
#pragma once
#include <X11/Xlib.h>
#include <string>
#include <vector>
#include "screen.hh"
namespace uw {
typedef unsigned char uchar;
typedef unsigned long ulong;
typedef struct Extent3D {
int width;
int height;
int depth;
} Extent3D;
class Image {
public:
Image(Screen &screen, const std::string imgPath);
~Image();
Visual *getVisual() { return v; }
XImage *getImage() { return image; }
GC getGC() { return gc; }
Pixmap getPixmap() { return pm; }
Extent3D getExtent() { return imageExtent; }
uchar *getImageData() { return data; }
std::string getImagePath() { return imagePath; }
private:
void createDepth();
void createPixmap();
void createImage();
void createGC();
Visual *v;
XImage *image;
GC gc;
Pixmap pm;
uchar *data = nullptr;
std::vector<uchar> cdata;
Extent3D imageExtent;
int chan;
int width;
int height;
int depth;
std::string imagePath;
Screen &uwScreen;
};
}

164
src/uw/orientation.cc Normal file
View File

@@ -0,0 +1,164 @@
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdexcept>
#include "orientation.hh"
namespace uw {
Orientation::Orientation(Screen &screen, Image &image)
: uwScreen{ screen }, uwImage{ image } {
}
Orientation::~Orientation() {}
void Orientation::center() {
Pixmap pm = XCreatePixmap(uwScreen.getDisplay(), uwScreen.getWindow(),
uwScreen.getExtent().width, uwScreen.getExtent().height,
uwImage.getExtent().depth);
XImage *img = XCreateImage(uwScreen.getDisplay(),
uwImage.getVisual(), uwImage.getExtent().depth, ZPixmap, 0, nullptr,
uwImage.getExtent().width, uwImage.getExtent().height, 32, 0);
if (!img) throw std::runtime_error("中央にする為にXImageの作成に失敗。");
std::vector<char>
imgData(uwImage.getExtent().width * uwImage.getExtent().height * 4);
img->data = imgData.data();
for (int y = 0; y < uwImage.getExtent().height; y++) {
for (int x = 0; x < uwImage.getExtent().width; x++) {
int idx = (y * uwImage.getExtent().width + x) * 4;
uchar blue = uwImage.getImageData()[idx + 2];
uchar green = uwImage.getImageData()[idx + 1];
uchar red = uwImage.getImageData()[idx + 0];
uchar alpha = uwImage.getImageData()[idx + 3];
ulong pixel = (alpha << 24) | (red << 16) | (green << 8) | blue;
XPutPixel(img, x, y, pixel);
}
}
XSetForeground(uwScreen.getDisplay(), uwImage.getGC(), 0);
XFillRectangle(uwScreen.getDisplay(), pm, uwImage.getGC(), 0, 0,
uwScreen.getExtent().width, uwScreen.getExtent().height);
int xoff = (uwScreen.getExtent().width - uwImage.getExtent().width) / 2;
int yoff = (uwScreen.getExtent().height - uwImage.getExtent().height) / 2;
if (xoff < 0) xoff = 0;
if (yoff < 0) yoff = 0;
XPutImage(uwScreen.getDisplay(), pm, uwImage.getGC(), img, 0, 0, xoff, yoff,
uwImage.getExtent().width, uwImage.getExtent().height);
XSetWindowBackgroundPixmap(uwScreen.getDisplay(), uwScreen.getWindow(), pm);
XClearWindow(uwScreen.getDisplay(), uwScreen.getWindow());
XFreePixmap(uwScreen.getDisplay(), pm);
img->data = nullptr;
XDestroyImage(img);
}
void Orientation::fill() {
Pixmap pm = XCreatePixmap(uwScreen.getDisplay(), uwScreen.getWindow(),
uwScreen.getExtent().width, uwScreen.getExtent().height,
uwImage.getExtent().depth);
XImage *img = XCreateImage(uwScreen.getDisplay(),
uwImage.getVisual(), uwImage.getExtent().depth, ZPixmap, 0, nullptr,
uwScreen.getExtent().width, uwScreen.getExtent().height, 32, 0);
if (!img) throw std::runtime_error("スケールする為にXImageの作成に失敗。");
std::vector<char> sdata(
uwScreen.getExtent().width * uwScreen.getExtent().height * 4);
img->data = sdata.data();
if (!img->data)
throw std::runtime_error("スケールしたデータにメモリの役割に失敗。");
double xScale = (double)uwScreen.getExtent().width / uwImage.getExtent().width;
double yScale = (double)uwScreen.getExtent().height / uwImage.getExtent().height;
for (int y = 0; y < uwScreen.getExtent().height; y++) {
for(int x = 0; x < uwScreen.getExtent().width; x++) {
int srcx = (x / xScale);
int srcy = (y / yScale);
if (srcx >= uwImage.getExtent().width) srcx = uwImage.getExtent().width - 1;
if (srcy >= uwImage.getExtent().height) srcy = uwImage.getExtent().height - 1;
int sidx = (srcy * uwImage.getExtent().width + srcx) * 4;
if (sidx >= uwImage.getExtent().width * uwImage.getExtent().height * 4)
continue;
uchar blue = uwImage.getImageData()[sidx + 2];
uchar green = uwImage.getImageData()[sidx + 1];
uchar red = uwImage.getImageData()[sidx + 0];
uchar alpha = uwImage.getImageData()[sidx + 3];
ulong pixel = (alpha << 24) | (red << 16) | (green << 8) | blue;
XPutPixel(img, x, y, pixel);
}
}
XClearWindow(uwScreen.getDisplay(), uwScreen.getWindow());
XPutImage(uwScreen.getDisplay(), pm, uwImage.getGC(), img, 0, 0, 0, 0,
uwScreen.getExtent().width, uwScreen.getExtent().height);
XSetWindowBackgroundPixmap(uwScreen.getDisplay(), uwScreen.getWindow(), pm);
XClearWindow(uwScreen.getDisplay(), uwScreen.getWindow());
XFreePixmap(uwScreen.getDisplay(), pm);
img->data = nullptr;
XDestroyImage(img);
}
void Orientation::tile() {
Pixmap pm = XCreatePixmap(uwScreen.getDisplay(), uwScreen.getWindow(),
uwScreen.getExtent().width, uwScreen.getExtent().height,
uwImage.getExtent().depth);
XImage *img = XCreateImage(uwScreen.getDisplay(),
uwImage.getVisual(), uwImage.getExtent().depth, ZPixmap, 0, nullptr,
uwImage.getExtent().width, uwImage.getExtent().height, 32, 0);
if (!img) throw std::runtime_error("タイルする為にXImageの作成に失敗。");
std::vector<char>
imgData(uwImage.getExtent().width * uwImage.getExtent().height * 4);
img->data = imgData.data();
for (int y = 0; y < uwImage.getExtent().height; y++) {
for (int x = 0; x < uwImage.getExtent().width; x++) {
int idx = (y * uwImage.getExtent().width + x) * 4;
uchar blue = uwImage.getImageData()[idx + 2];
uchar green = uwImage.getImageData()[idx + 1];
uchar red = uwImage.getImageData()[idx + 0];
uchar alpha = uwImage.getImageData()[idx + 3];
ulong pixel = (alpha << 24) | (red << 16) | (green << 8) | blue;
XPutPixel(img, x, y, pixel);
}
}
XPutImage(uwScreen.getDisplay(), pm, uwImage.getGC(), img, 0, 0, 0, 0,
uwImage.getExtent().width, uwImage.getExtent().height);
XSetWindowBackgroundPixmap(uwScreen.getDisplay(), uwScreen.getWindow(), pm);
XClearWindow(uwScreen.getDisplay(), uwScreen.getWindow());
for (int y = 0; y < uwScreen.getExtent().height; y += uwImage.getExtent().height) {
for (int x = 0; x < uwScreen.getExtent().width; x += uwImage.getExtent().width) {
XCopyArea(uwScreen.getDisplay(), pm, uwScreen.getWindow(), uwImage.getGC(),
0, 0, uwImage.getExtent().width, uwImage.getExtent().height, x, y);
}
}
XFreePixmap(uwScreen.getDisplay(), pm);
img->data = nullptr;
XDestroyImage(img);
}
}

20
src/uw/orientation.hh Normal file
View File

@@ -0,0 +1,20 @@
#pragma once
#include "screen.hh"
#include "image.hh"
namespace uw {
class Orientation {
public:
Orientation(Screen &screen, Image &image);
~Orientation();
void center();
void fill();
void tile();
private:
Screen &uwScreen;
Image &uwImage;
};
}

21
src/uw/screen.cc Normal file
View File

@@ -0,0 +1,21 @@
#include <X11/Xlib.h>
#include <stdexcept>
#include "screen.hh"
namespace uw {
Screen::Screen() {
dp = XOpenDisplay(nullptr);
if (dp == nullptr) throw std::runtime_error("X画面を開くに失敗。");
screen = DefaultScreen(dp);
w = RootWindow(dp, screen);
extent.width = DisplayWidth(dp, screen);
extent.height = DisplayHeight(dp, screen);
}
Screen::~Screen() {
XCloseDisplay(dp);
}
}

29
src/uw/screen.hh Normal file
View File

@@ -0,0 +1,29 @@
#pragma once
#include <X11/Xlib.h>
namespace uw {
typedef struct Extent2D {
int width;
int height;
} Extent2D;
class Screen {
public:
Screen();
~Screen();
Display *getDisplay() { return dp; }
Window getWindow() { return w; }
int getScreen() { return screen; }
Extent2D &getExtent() { return extent; }
private:
Display *dp;
Window w;
int screen;
Extent2D extent;
};
}

23
uw.1 Normal file
View File

@@ -0,0 +1,23 @@
.TH UW 1 VERSION
.SH NAME
uw - Unix Wallpaper
.br
.B uw
[-cft \fI\,image\fR]
.SH 説明
.PP
Unixの背景設定ツールです。
.br
パラメーターなしで実行すると、GUI版旧uw-setterを起動します。
.TP
\fB\,c\fR \fI\,image\fR
画面の中央に背景画像を設定します。
.TP
\fB\,f\fR \fI\,image\fR
画像サイズに関係なく、全画面に背景画像を設定します。
.TP
\fB\,t\fR \fI\,image\fR
画像をタイル状に配置して背景画像を設定します。
.SH AUTHORS
.PP
テクニカル諏訪子(開発者)