1
0
mirror of https://github.com/gryf/wmaker.git synced 2026-07-07 11:16:38 +02:00

Compare commits

...

53 Commits

Author SHA1 Message Date
David Maciejak cc0a652ea8 WPrefs: add mouse wheel action to the expert panel
This patch is adding the mouse wheel action to be performed
on the expert text frame panel.
2026-02-16 17:55:21 +00:00
David Maciejak 80079cd343 WINGs: improve wlist widget
This patch is adding keyboard control to the wlist widget
(up/down/pgup/pgdw/home/end) and typeahead list search.
That component is for example used in the wmaker icon chooser,
and WPrefs keyboard shortcut, font conf panels.
2026-02-16 17:55:21 +00:00
David Maciejak 955c6793a6 WINGs: fix memory leaks and potential buffer over-read in wfont
This patch is fixing memory leaks when pango structures are used
but not freed.
Also according to commit 4f050ebab9,
previous_text string in WMWidthOfString can be not NULL terminated,
the same construct is used in WMDrawString and WMDrawImageString
functions, so to be safe better to also check for the length
of the string.
2026-02-16 01:41:16 +00:00
David Maciejak 189679b49c wmaker: don't exit wmaker if app child process errored
This patch is fixing a possible memory corruption and abnormal exit
of wmaker when a created child process errored.
This had been kind of mentioned at
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1040643
and I also experienced it with especially Steam.
At first, I also thought it was a crash or a memory corruption,
but gdb or valgrind are not reporting anything.
In fact, Steam is not setting properly some hints and that is bringing
some issues. More patches will be needed to support that app properly.
For example, WM_COMMAND is not set properly and when you are trying to
relaunch the app from the appIcon the child process is generating an error
and the actual code is calling Exit(-1) which is entirely exiting wmaker,
that's why from gdb you can see the message
[Inferior 4 (process 567278) exited normally] and no crash.
2026-02-15 20:38:59 +00:00
David Maciejak e45a3bc07d wmaker: ignore mouse wheel when giving window focus
This patch is fixing a window focus issue by ignoring mouse wheel
up/down buttons. How to reproduce the issue:
open 2 xterm with one window overlapping the other, in 1 xterm list
files with ls to have the scrollbar to appear, click the other xterm
to give focus to its window, now mouse wheel up or down on the other
xterm, at that point the window is focused but under the other xterm.
Try to click left button to get it to appear on top, it will not work
cause the focus is set already on it. With that patch the click to
focus is working.
2026-02-14 16:10:30 +00:00
David Maciejak bc16a03722 WINGs: fix compiler warnings on fontl example
The patch is fixing some compiler warnings about missing
noreturn keyword.
2026-02-14 16:10:30 +00:00
David Maciejak 260415eb42 wmaker: treat empty icon name hint as unset
This patch is treating empty _NET_WM_ICON_NAME as unset,
thus the code is falling back to set the appicon title
to the window title. Case seen with virtualbox where
the _NET_WM_ICON_NAME(UTF8_STRING) is set to empty string.
2026-02-13 09:06:07 +00:00
David Maciejak c8174a4b2e wmaker: add room for the modelock option in winspector
This patch is conditionaly adding some extra room in the window
inspector advanced options frame if modelock is enabled to fit
the extra option. Without it the option text is truncated.
2026-02-11 22:02:05 +00:00
David Maciejak 2901b418ad wmaker: add window app icon to the window list
This patch is adding the app icon in between the flags icon and
the window name from the window list.
Feature request from https://github.com/window-maker/wmaker/issues/19
It is disabled by default, it needs WindowListAppIcons to be
set to YES manually in the conf file or "Show app icons in window list."
enabled from WPrefs expert panel.
2026-02-10 09:35:23 +00:00
David Maciejak ae6b03e50f wmiv: fix default window size
Set default windows size to 10x10 as 1x1 is unmanaged since
commit 839061a25a.
2026-02-09 09:23:07 +00:00
David Maciejak 128ba2ddc7 wmaker: use appicon icons in the switchpanel when existing
This patch is making sure the icons shown in the switchpanel
are also those used for the appicon. For example, for xterm
the icon used in the switchpanel is the default app and not the
icon provided by xterm app.
Seems that issue is also present in vanilla wmaker 0.96.
2026-02-09 09:23:07 +00:00
David Maciejak 7ca96b9ed6 wmaker: fix titlebar glitch issues with modelock
This patch is fixing 2 issues with the modelock language pixmap
located in the titlebar.

If wmaker is compiled with modelock support but modelock is disabled
from the expert preferences, dialog windows like run command or exit
will not show the language pixmap but will show empty frame borders.

When modelock is enabled from the expert preferences, it needs a
warm restart for the titlebars to be updated with the language pixmap.
If afterwards, modelock is disabled from the expert preferences,
any old windows that is gettting focus will be repainted and the language
pixmap will be destroyed but the empty frame borders will still be
present. Now to fully disable modelock for existing opened windows,
wmaker needs a warm restart.
2026-02-09 09:23:07 +00:00
David Maciejak 839061a25a wmaker: fix window position issues
This patch is fixing some initial window position issues
as seen for example with virtualbox.
Now wmaker is not trying to manage the 1x1 app internal windows
which was resulting on some position issues (stacking the window
app on the left side).
It also fixes some window jitters issues when the app is trying
to negotiate some tiny position adjustments.
Related to bug at https://github.com/window-maker/wmaker/issues/26
2026-02-07 20:39:24 +00:00
David Maciejak 1e63c590b6 wmaker: factorize duplicated code
This patch is factorizing is_same and getBool functions in misc.
is_same is renamed to WMStrEqual
getBool is renamed to WMPLGetBool
to prevent name collision issues.
2026-02-07 20:37:46 +00:00
David Maciejak 92e1e9fb0b WPrefs: fix some truncated texts
This patch is fixing some text truncation in the ergonomic and
hot corner preference tabs.
2026-02-07 13:33:40 +00:00
David Maciejak ddf0b20367 wmaker: refactor get_corner()
This patch refactors get_corner() for better readability
and performance.
2026-02-07 13:33:40 +00:00
David Maciejak d303317a31 wmaker: handle keybinding change notifications
This patch is to fix an issue seen on FreeBSD 15 where
keybinding are mixed up at the cold start of wmaker.
It is mentioned at https://github.com/window-maker/wmaker/issues/43
Seems that issue is not happening on Linux.
A warm restart ("restart window maker") from the root menu
is getting rid of that issue temporarily.

To solve that issue, now wmaker is reloading the keyboard mapping
via the new wReadKeybindings function when a XkbNewKeyboardNotifyMask
event is received.
It means xkb, which is part of X11 core,
is now used by default and not conditionally with modelock.
I tried to delay reading the keybinding as late as possible but
it did not solve the issue as seems X is started with a improper
keyboard by default.

Here some debug trace when the bindings are loaded by wmaker on FreeBSD:

Keybind F12: keycode=96 modifier=0x0   <--- cold starting wmaker
Keybind F11: keycode=95 modifier=0x0
Keybind Escape: keycode=9 modifier=0x4
Keybind M: keycode=58 modifier=0x8
Keybind H: keycode=43 modifier=0x8
Keybind Up: keycode=98 modifier=0x8   <--- keycode is wrong, provided by X11
Keybind Down: keycode=104 modifier=0x8
Keybind Tab: keycode=23 modifier=0x8
Keybind Tab: keycode=23 modifier=0x9
Keybind Right: keycode=102 modifier=0xc
Keybind Left: keycode=100 modifier=0xc
Keybind 1: keycode=10 modifier=0x8
Keybind 2: keycode=11 modifier=0x8
Keybind 3: keycode=12 modifier=0x8
Keybind 4: keycode=13 modifier=0x8
Keybind 5: keycode=14 modifier=0x8
Keybind 6: keycode=15 modifier=0x8
Keybind 7: keycode=16 modifier=0x8
Keybind 8: keycode=17 modifier=0x8
Keybind 9: keycode=18 modifier=0x8
Keybind 0: keycode=19 modifier=0x8
Keybind Print: keycode=111 modifier=0x0   <--- keycode is wrong, 111 is UP key

/usr/ports/x11-wm/windowmaker/work/WindowMaker-0.96.0/src/.libs/wmaker(execInitScript(main.c:531)):
error: /root/GNUstep/Library/WindowMaker/autostart:could not execute initialization script   <--- warm restart from wmaker
Keybind F12: keycode=96 modifier=0x0
Keybind F11: keycode=95 modifier=0x0
Keybind Escape: keycode=9 modifier=0x4
Keybind M: keycode=58 modifier=0x8
Keybind H: keycode=43 modifier=0x8
Keybind Up: keycode=111 modifier=0x8   <--- UP key keycode is correct
Keybind Down: keycode=116 modifier=0x8
Keybind Tab: keycode=23 modifier=0x8
Keybind Tab: keycode=23 modifier=0x9
Keybind Right: keycode=114 modifier=0xc
Keybind Left: keycode=113 modifier=0xc
Keybind 1: keycode=10 modifier=0x8
Keybind 2: keycode=11 modifier=0x8
Keybind 3: keycode=12 modifier=0x8
Keybind 4: keycode=13 modifier=0x8
Keybind 5: keycode=14 modifier=0x8
Keybind 6: keycode=15 modifier=0x8
Keybind 7: keycode=16 modifier=0x8
Keybind 8: keycode=17 modifier=0x8
Keybind 9: keycode=18 modifier=0x8
Keybind 0: keycode=19 modifier=0x8
Keybind Print: keycode=107 modifier=0x0   <--- Print keycode is correct

Alternatively, to mitigate the issue, .xinitrc can be set to:

setxkbmap -layout us
exec wmaker

or whatever layout you are using.
2026-02-07 13:33:40 +00:00
David Maciejak c620b354b5 wmaker: fix some truncated texts in the info panel
This patch is fixing some text truncation especially
for displaying the total memory allocated in debug mode
and the image formats (when the JXL support is enabled).
2026-02-07 13:33:40 +00:00
David Maciejak ebac2bbcd8 wmaker: fix compiler warning in properties.c
This patch is fixing a compiler warning for the implicit
conversion of int to char changes value from 255 to -1
when at the line *ptr++ = 255 the code is trying to store
the value 255 into a char.
2026-02-07 13:33:40 +00:00
David Maciejak 3281349f8d WINGs: remove unnecessary warning in wtextfield
Since commit 77db6dc649
("WINGs: fix right and center aligned wtextfield"),
right and center alignments are working in wtextfield.
2026-02-07 13:32:27 +00:00
David Maciejak 3f5280987e WINGs: refactor wlist
This patch refactors wlist to bring some improvements
to not count the list of entries at each loop iteration.
2026-02-07 13:31:28 +00:00
David Maciejak 6d0953bc22 wmaker: add support for _NET_WM_MOVERESIZE
This patch adds support for _NET_WM_MOVERESIZE hint as defined in EWMH
which allows a window without decorations to manage itself (moving/resizing).
The purpose is to fix the issue mentioned at https://github.com/window-maker/wmaker/issues/20

Tested with VS Code, Google Chrome, Steam and Discord.

Specs are available at https://specifications.freedesktop.org/wm/1.5/ar01s04.html#id-1.5.4
2026-02-03 18:05:20 +00:00
David Maciejak ef1a504898 wrlib: alpha combine speed improvement
This patch is improving the alpha combine function by using int
instead of float. That function is used for example in the
switch panel to merge the transparency mask.
The change is practically indistinguishable to the human eye
for a single-pass blend but the performance gained is huge.

I've been doing some benchmark of wrlib and even implemented AVX2 support.
But the gain compared to the complexity of AVX2 is not worth,
while having int usage in that specific function is a really good trade-off.

Here the result:

Alpha Blending Performance Test
Image size: 1024x768 (786432 pixels)
Iterations: 100

AVX2 support: YES

=== RGBA Source Test ===
Original (float):     2.540 ms/frame  (393.8 FPS)
Optimized (int):      1.983 ms/frame  (504.2 FPS)  [1.3x speedup]
AVX2 optimized:       1.843 ms/frame  (542.6 FPS)  [1.4x speedup]

By using int, the alpha blending in that use case is 28% faster.
2026-01-27 10:56:30 +00:00
David Maciejak 474b23344a wrlib: change default scaling interpolation to Catmull-Rom
This patch is implementing a new default Catmull-Rom filter
to resize images.
Catmull-Rom is a special case of cubic interpolation with B=0 and
C=0.5 (in the Mitchell-Netravali formulation).
It provides slighlty sharper results than Mitchell with the same
performance.
Catmull-Rom is a better choice for a window manager as it prioritizes
sharpness, making small elements feel crisp.
2026-01-27 10:53:34 +00:00
David Maciejak 77db6dc649 WINGs: fix right and center aligned wtextfield
This patch is fixing some issues in how right and center aligned
wtextfields are handled.
-text selection with mouse was not working properly especially
setting and identifying the cursor position
-middle button paste was only working for left aligned text
2026-01-27 10:52:04 +00:00
David Maciejak b09ac30233 WINGs: add case to wtextfield test
This patch adds a wtextfield center aligned to the unit test.
2026-01-27 10:50:58 +00:00
David Maciejak 6e14b6142b wmaker: fix _NET_WM_NAME window manager's name
xterm is not working properly (it's not advertising its internal icon)
if the window manager's name contains a space, seems to be specific
to xterm as xeyes and xpaint are working fine.
2026-01-27 10:47:58 +00:00
David Maciejak 7778df2fc5 wmaker: add keyboard control to crash window
This patch is allowing to control the wpopupbutton entries
via keyboard up/down arrows.
It happens to me a few weeks ago during development,
wmaker crashed and I lost the mouse control.
I was stuck on that dialog box without a way to select another entry.
2026-01-25 01:28:53 +00:00
David Maciejak 156841f80e WINGs: wpopupbutton add two functions to change the displayed item
This patch is adding WMSelectPopUpButtonPreviousItem and
WMSelectPopUpButtonNextItem functions to the widget.
The WINGs lib version is bumped.
2026-01-25 01:28:53 +00:00
David Maciejak c82e6dad5c wmaker: set the window manager's information
According to EWMH specification, the active window manager is supposed
to set some information. Those can be gathered for example with
'wmctrl -m'.

Before the patch:
$ wmctrl -m
Name: N/A
Class: N/A
PID: N/A
Window manager's "showing the desktop" mode: OFF

After the patch:
$ wmctrl -m
Name: Window Maker 0.96.0
Class: wmaker
PID: 6866
Window manager's "showing the desktop" mode: OFF
2026-01-24 14:24:03 +00:00
David Maciejak 95c68fe26d configure: add optional --enable-native option to compilation flags
This patch adds an option to enable native CPU optimizations
by adding -march=native to the compiler flags, tuning the
generated code for the build machine at the expense of portability.

In some tests, especially on wrlib I saw 8% perf improvement.
Should be used by developers or those who recompile wmaker for their own usage.
2026-01-24 14:24:03 +00:00
David Maciejak 77c3b95fd5 Doc: fix wmiv man page
Sort out the argument order or else 'make check' will complain.
2026-01-24 00:52:25 +00:00
David Maciejak b662d0827c WINGs: wfont fallback to default font when font is not found
This patch is fixing the issue reported
at https://github.com/window-maker/wmaker/issues/62
where wmaker is crashing if the font specified in WMGLOBAL is not found.
Now by default it will failsafe to DEFAULT_FONT.
2026-01-22 22:46:10 +00:00
David Maciejak a542934e67 WINGs: fix cursor ghosting issue in wtextfield
The cursor is moved using right/left arrows but as it's blinking
and a XOR function is used to hide/unhide it, it happens the cursor
can be not hidden properly from its previous position.
Better to refresh the view to avoid such issues.
2026-01-22 22:46:10 +00:00
David Maciejak d2b2c3238b WINGs: fix cursor position in wtextfield
In case the cursor is positioned out of the textfield view
after a delete and more chars are entered, wmaker process
will reach 100% and become unresponsive.

How to reproduce:
in the run command window enter an overly long text (longer than
the current input field view). Then, press home to go back to the
beginning of the string. Then, shift-End to select all the text,
then del to delete all the text. At that point the cursor is still
out of the view and if you enter more text wmaker process will be stuck.
2026-01-22 22:46:10 +00:00
David Maciejak d37a3162e0 WINGs: fix TARGETS request return type
According to the ICCCM, a reply to a TARGETS request must be a list of atoms.
Took the chance to also fix variable naming consistency between wtext and
wtextfield which are using the same kind of code.
2026-01-22 22:46:10 +00:00
David Maciejak 9ddacfc29b Fix typos and some appicon consistency in the FAQ 2026-01-18 14:54:28 +00:00
David Maciejak 7e21785710 wmiv: add support for archive files and a new option to ignore unknown image format
This patch adds optional support for compressed files and a new --ignore-unknown option
to ignore unknown image format. It also adds local filename drag-and-drop support.
And a copy current image to clipboard feature with ctrl+c shortcut.

It also fixes:
- fullscreen issue on multi monitors setup by using randr
- app icon advertised via _NET_WM_ICON
- fix UTF-8 filename usage in window title
2026-01-17 22:03:09 +00:00
David Maciejak 7cfdf6bd68 WRaster: add function to save image in memory
This patch adds the RSaveRawImage() function to the WRaster lib
to be able to save image structure in memory.
The WRaster lib version is bumped.
2026-01-17 22:03:09 +00:00
David Maciejak 72942267aa Prevent wrong UTF-8 window title truncation
The patch is fixing a UTF-8 truncation issue that could happen with the window title
when it was shrinked to be displayed in the window list (F11), leading to
an infinite loop.

Issue was reported at https://github.com/window-maker/wmaker/issues/61
2026-01-15 15:56:20 +00:00
Balló György ac19c4bc07 Don't set X-GDM-SessionRegisters to true
Window Maker does not call the RegisterSession() method on GDM's D-Bus
interface, causing that GDM doesn't know that the login was successful,
which leads to problems. If X-GDM-SessionRegisters is not specified or
false, GDM registers the session itself.

I noticed this when I logged out from Window Maker, and tried to log
in into another session with GDM, it doesn't work, because the login
screen hangs as the previous session was not entered into registered
state within GDM.

If X-GDM-SessionRegisters=true is specified, GDM expects that the
session will be registered via D-Bus:
https://gitlab.gnome.org/GNOME/gdm/-/commit/1c061b84ffc3e874da825982d18d970556ff74bb

E.g. GNOME Shell calls RegisterSession() method after login:
https://gitlab.gnome.org/GNOME/gnome-shell/-/commit/0e37cd2ec92c5fbbc0870272e5e00fc07a705eba

Older versions behave the same way as would be X-GDM-SessionRegisters
not set or false.

Currently X-GDM-SessionRegisters=true is used only by GNOME Shell. All
other sessions omit this property, because they don't call the
RegisterSession() method on GDM's D-Bus interface after login.
2026-01-13 13:11:21 +00:00
David Maciejak 14bd13b1be Doc: fix spelling errors 2026-01-04 14:57:50 +00:00
David Maciejak afe13d3e72 New expert option: cycle all windows from all workspaces
Mentioned on the WMaker user mailing list, option disabled by default.

https://groups.google.com/g/wmaker-user/c/pR8P-ZYCDFo/m/Wo42U_xqBgAJ

So basically the patch is adding a new expert option in WPrefs (disabled by
default) to allow the switch panel to cycle over all the windows from all
workspaces. I believe it's useful if you are using a lot of fullscreen apps
each on different workspaces.
2026-01-03 19:23:34 +00:00
Carlos R. Mafra e356ef8c05 Revert "Prevent unnecessary RandR wmaker restart"
This reverts commit 3579c85af1.

As pointed out by David Maciejak himself, this patch triggers
an odd behavior:

 "Now I cannot do a rectangular selection on the desktop with the left
 click of the mouse like I used to do.
 Seems the XRRQueryVersion call to get randr version is messing up with the
 X events. I tried to move up the call in src/startup.c and src/main.c
 instead to the point where the bug cannot be reproduced if I am putting the
 XRRQueryVersion code just before the call to wDefaultsInitDomain
 "WMWindowAttributes" (in src/startup.c) which is really weird."
2025-12-30 20:25:46 +00:00
David Maciejak 3d993a7323 Fix UTF-8 usage in icon title
When pango is enabled and a window title contains UTF-8 chars,
there is a chance the miniaturized icon title will cut in 2 an UTF-8 char
leading to a glitch in the title.
2025-12-29 16:10:24 +00:00
David Maciejak a4a6e65ca9 Fix for ignore client supplied icon attributes
As mentioned on the WMaker user mailing list some time ago
https://groups.google.com/g/wmaker-user/c/95M_pb_Qlbs/m/6qJLJSqoAwAJ
The Ignore client supplied icon from the windows attributes is not working.
That's especially visible with firefox and thunderbird when they are using
NET_WM_ICON to push the embedded icon.
That patch is making sure to ignore the embedded icon if the user defined one.
2025-12-29 16:10:24 +00:00
David Maciejak 4392fdc291 WRaster: Add optional support to JPEG XL
Detect if libjxl is intalled, and build-in support in raster lib.
Feature can be disabled/enabled at configure time.
2025-12-27 19:48:37 +00:00
David Maciejak 67e2f5e1ca Prevent XAllocColor() call on TrueColor display
As reported in bug https://github.com/window-maker/wmaker/issues/50
X11 XAllocColor() call from wGetColorForColormap() in src/resources.c is returning some errors
especially seen when running GZDoom.

TrueColor display has been the dominant standard for well over a decade, meaning almost all modern X servers default to a TrueColor visual.
The default colormap is predefined and read-only, making allocation with XAllocColor() unnecessary (and meaning no need to free it too).
The patch is checking the display visual, and in case the display is truecolor, it does not allocate or free the color, just looking up for it.
Once the patch applied, GZDoom is not reporting warnings anymore.
2025-12-25 23:22:57 +00:00
David Maciejak 3579c85af1 Prevent unnecessary RandR wmaker restart
As reported in bug https://github.com/window-maker/wmaker/issues/34
when RandR is enabled, everytime a monitor is turned on or off wmaker is restarting
(that happens either manually or automatically with DPMS).
This behavior causes issues with xscreensaver.

How to reproduce the issue:
-install xscreensaver and lock the screen
-turn off then on the monitor manually
-you can see that wmaker was restarted and behind the xscreensaver lock window,
the desktop appears, potentially leaking information

Instead of using the RandR event RRScreenChangeNotifyMask which is too generic,
the patch is using RRCrtcChangeNotifyMask defined since RandR 1.2 (released in 2007).
In the recent RandR version, events for output (hardware) changes are propagated via
RROutputChangeNotifyMask while layout changes (like position, size, rotation)
are propagated via RRCrtcChangeNotifyMask.

The patch is purposedly not listening for RROutputChangeNotifyMask,
thus wmaker is not restarting on DPMS events anymore.

Currently, in case a new monitor is added (or removed) wmaker is not discovering it anyway
even after an automatic restart.
Either, wmaker has to be exited and restarted fully or an external tool like arandr
has to be used to configure the new monitor. So after the patch functionality remains unchanged.
2025-12-24 12:37:12 +00:00
David Maciejak 896bbce2e5 Remove default print key grabbing 2025-12-21 21:23:30 +00:00
David Maciejak 8f1ee27595 Remove duplicated code 2025-12-21 21:23:13 +00:00
Страхиња Радић 541947e9ef Update translator's email for Serbian
This patch updates my email throughout the source.

Signed-off-by: Страхиња Радић <sr@strahinja.org>
2025-10-27 20:47:25 +00:00
Страхиња Радић 7bb7d0f4d6 m4/wm_i18n.m4: Make compatible with POSIX sed
Currently, it seems like the script m4/wm_i18n.m4 is passing some editing
commands to sed(1), which are not strictly conforming to POSIX[1]. Namely, the
"grouping" command:

	{ command; command }

needs to have either a semicolon or a newline before the closing brace:

	{ command; command; }

or

	{ command; command
	}

according to POSIX.

On systems which don't use the lax GNU sed by default (like OpenBSD), the
current configuration and compilation goes like this:

$ autoreconf -vif

	...
	sed: 1: "/po$/{s,.po,,;p}": extra characters at the end of p command
	sed: 1: "/po$/{s,.po,,;p}": extra characters at the end of p command
	... (etc)

$ ./configure --without-menu-textdomain CATALOGS=sr.mo LINGUAS=sr \
	LIBS=-lintl MSGFMT=msgfmt --mandir=/usr/local/man

	...

	Translated languages to support     :

	configure: WARNING: No language from $LINGUAS are supported

$ gmake && doas gmake install
	... (no .mo files are generated nor installed)

and so on, since the editing commands in question are affecting the processing
of .po files.

This patch proposes inserting semicolons before the closing brace in the
mentioned editing commands passed to sed(1).

[1]: https://pubs.opengroup.org/onlinepubs/9799919799/utilities/sed.html#tag_20_109_13_03

Signed-off-by: Страхиња Радић <sr@strahinja.org>
2025-10-27 20:47:25 +00:00
76 changed files with 5432 additions and 747 deletions
+11 -11
View File
@@ -15,7 +15,7 @@ Summary of Contents:
1.2 Where can I get Window Maker?
1.3 Where are the mailing list archives?
1.4 Where is more documentation on configuring Window Maker?
1.5 What is an App Icon?
1.5 What is an appicon?
1.6 How can I get a question added to the FAQ?
1.7 How do I report bugs?
1.8 Is there an anonymous cvs server?
@@ -134,7 +134,7 @@ Introduction:
1.1 What is Window Maker?
----------------------------------
Window Maker is an elegant X11 window manger that emulates the
Window Maker is an elegant X11 window manager that emulates the
OpenStep desktop with some noticeable enhancements. Look at
http://wm.current.nu/themes/my-themes.html and
http://wm.current.nu/themes/screenshots.html
@@ -507,7 +507,7 @@ exec wmaker
Things to try if it doesn't work: (somewhat fuzzy and random)
This should do it although I did have problems sometimes initially which I
fixed by randomly trying absolute pathes for wmaker in .xsession/.xinitrc and/or
fixed by randomly trying absolute paths for wmaker in .xsession/.xinitrc and/or
making the dtprofile/.xinitrc/etc executable. It helps logging in on the console
(select from CDE login screen) and start X manually using 'X'.
If it works that way it should work when logging into the CDE environment.
@@ -628,7 +628,7 @@ the last app started by .xinitrc is closed.
the X server will be closed, and your session will end.
3.3 How do I "dock" AppIcons on the Clip?
3.3 How do I "dock" appicons on the Clip?
-----------------------------------------
Just drag icons near it like you would for the dock. Note that if you place
the clip near the dock, you may have problems docking appicons in either.
@@ -674,7 +674,7 @@ David Reviejo <dreviejo@arrakis.es> best summed up this answer:
3.7 How do I "undock" AppIcons?
3.7 How do I "undock" appicons?
----------------------------------
If the program is not running, just drag the icon to the middle of your
desktop (away from the dock and clip) and watch it disappear.
@@ -731,7 +731,7 @@ These directories will be listed under the "Search Path" section of WPrefs.
3.11 When I run GNOME application they have no title bar.
----------------------------------
The GNOME toolkit decided to behave his own way, so it draws its own title bar
The GNOME toolkit decided to behave in its own way, so it draws its own title bar
with buttons, and asks Window Maker to not draw anything (title bar, resize bar,
close and miniaturise button, ...).
@@ -838,7 +838,7 @@ AlwaysUserIcon=Yes;
};
4.6 How do I get a Tile background for my AppIcons (those things in
4.6 How do I get a Tile background for my appicons (those things in
the dock)?
----------------------------------
You need to change 1 line to your ~/GNUstep/Defaults/WindowMaker file.
@@ -868,9 +868,9 @@ Application Icon'' Option then Save, Apply and close the dialog.
This should allow you do dock the program normally.
Emulate Appicon does exactly the same thing as dockit, a small app
Emulate AppIcon does exactly the same thing as dockit, a small app
distributed with ancient versions of Window Maker before the Attribute
Editor existed (now deprecated for the obvious reason). If Emulate Appicon
Editor existed (now deprecated for the obvious reason). If Emulate AppIcon
does not work, dockit will not work either. Such applications violate the
ICCCM (Inter-Client Communication Conventions Manual) and are considered
badly coded for not setting the instance.class hints.
@@ -898,7 +898,7 @@ NoAppIcon=Yes;
4.9 How do I set a pixmap background?
----------------------------------
Here is the in depth explanation straight from the NEWS file:
Here is the in-depth explanation straight from the NEWS file:
wmsetbg now accepts the following options:
usage: wmsetbg [-options] image
@@ -1476,7 +1476,7 @@ Themes:
8.1 What exactly are themes?
----------------------------------
Themes are a great aspect of Window Maker allowing a user to simply
save the entire 'look' of their desktop in a Archive to distribute freely among
save the entire 'look' of their desktop in an archive to distribute freely among
friends, fellow users and/or the whole net in general. :)
See the theme-HOWTO at http://wm.current.nu/themes/theme-HOWTO.html
+3 -2
View File
@@ -24,8 +24,9 @@
#include <WINGs/WINGs.h>
#include <WINGs/WUtil.h>
#include <inttypes.h>
#include <stdnoreturn.h>
void wAbort()
noreturn void wAbort(void)
{
exit(0);
}
@@ -40,7 +41,7 @@ void show(WMWidget * self, void *data)
WMSetLabelText(l, buf);
}
void quit(WMWidget * self, void *data)
noreturn void quit(WMWidget * self, void *data)
{
(void) self;
(void) data;
+6 -1
View File
@@ -521,7 +521,7 @@ void testSlider(WMScreen * scr)
void testTextField(WMScreen * scr)
{
WMWindow *win;
WMTextField *field, *field2;
WMTextField *field, *field2, *field3;
windowCount++;
@@ -540,6 +540,11 @@ void testTextField(WMScreen * scr)
WMMoveWidget(field2, 20, 50);
WMSetTextFieldAlignment(field2, WARight);
field3 = WMCreateTextField(win);
WMResizeWidget(field3, 200, 20);
WMMoveWidget(field3, 20, 80);
WMSetTextFieldAlignment(field3, WACenter);
WMRealizeWidget(win);
WMMapSubwidgets(win);
WMMapWidget(win);
+6
View File
@@ -1462,6 +1462,10 @@ void WMSetPopUpButtonEnabled(WMPopUpButton *bPtr, Bool flag);
Bool WMGetPopUpButtonEnabled(WMPopUpButton *bPtr);
int WMSelectPopUpButtonPreviousItem(WMPopUpButton *bPtr);
int WMSelectPopUpButtonNextItem(WMPopUpButton *bPtr);
/* ---[ WINGs/wprogressindicator.c ]------------------------------------- */
WMProgressIndicator* WMCreateProgressIndicator(WMWidget *parent);
@@ -1529,6 +1533,8 @@ void WMSetScrollViewRelief(WMScrollView *sPtr, WMReliefType type);
WMRect WMGetScrollViewVisibleRect(WMScrollView *sPtr);
void WMScrollViewScrollPoint(WMScrollView * sPtr, WMPoint point);
WMScroller* WMGetScrollViewHorizontalScroller(WMScrollView *sPtr);
WMScroller* WMGetScrollViewVerticalScroller(WMScrollView *sPtr);
+1 -1
View File
@@ -9,7 +9,7 @@ sk.po Slovak Jan 'judas' Tomka <judas@linux.sk>
bg.po Bulgarian Anton Zinoviev <zinoviev@debian.org>
nl.po Dutch Alwin <translations@ziggo.nl>
fy.po Frisian Alwin <translations@ziggo.nl>
sr.po Serbian Strahinya Radich <contact@strahinja.org>
sr.po Serbian Strahinya Radich <sr@strahinja.org>
Notes
-----
+2 -2
View File
@@ -3,7 +3,7 @@
# 1998-2006 Dan Pascu
# 2013-2020 Window Maker Developers Team
# This file is distributed under the same license as the Window Maker package.
# Strahinya Radich (Страхиња Радић) <contact@strahinja.org>, 2023.
# Strahinya Radich (Страхиња Радић) <sr@strahinja.org>, 2023.
#
msgid ""
msgstr ""
@@ -11,7 +11,7 @@ msgstr ""
"Report-Msgid-Bugs-To: wmaker-dev@googlegroups.com\n"
"POT-Creation-Date: 2023-11-02 19:36+0100\n"
"PO-Revision-Date: 2023-11-02 19:48+0100\n"
"Last-Translator: Страхиња Радић <contact@strahinja.org>\n"
"Last-Translator: Страхиња Радић <sr@strahinja.org>\n"
"Language-Team: Serbian <sr@li.org>\n"
"Language: sr\n"
"MIME-Version: 1.0\n"
+37 -33
View File
@@ -70,37 +70,30 @@ static Bool hasProperty(FcPattern * pattern, const char *property)
return False;
}
static Bool hasPropertyWithStringValue(FcPattern * pattern, const char *object, const char *value)
{
FcChar8 *str;
int id;
if (!value || value[0] == 0)
return True;
id = 0;
while (FcPatternGetString(pattern, object, id, &str) == FcResultMatch) {
if (strcasecmp(value, (char *)str) == 0) {
return True;
}
id++;
}
return False;
}
static char *makeFontOfSize(const char *font, int size, const char *fallback)
{
FcPattern *pattern;
FcPattern *pattern = NULL;
char *result;
if (font[0] == '-') {
if (font && font[0] == '-') {
pattern = xlfdToFcPattern(font);
} else {
pattern = FcNameParse((const FcChar8 *) font);
}
/*FcPatternPrint(pattern); */
if (!pattern) {
wwarning(_("could not load font spec: %s."), font);
if (!fallback)
return NULL;
pattern = FcPatternCreate();
if (!pattern)
return NULL;
if (!FcPatternAddString(pattern, FC_FAMILY, (const FcChar8 *) fallback)) {
wfatal(_("could not load default font spec: %s."), fallback);
FcPatternDestroy(pattern);
return NULL;
}
}
if (size > 0) {
FcPatternDel(pattern, FC_PIXEL_SIZE);
@@ -109,12 +102,6 @@ static char *makeFontOfSize(const char *font, int size, const char *fallback)
FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)DEFAULT_SIZE);
}
if (fallback && !hasPropertyWithStringValue(pattern, FC_FAMILY, fallback)) {
FcPatternAddString(pattern, FC_FAMILY, (const FcChar8 *) fallback);
}
/*FcPatternPrint(pattern); */
result = (char *)FcNameUnparse(pattern);
FcPatternDestroy(pattern);
@@ -135,7 +122,7 @@ WMFont *WMCreateFont(WMScreen * scrPtr, const char *fontName)
double size;
#endif
if (fontName[0] == '-') {
if (fontName && fontName[0] == '-') {
fname = xlfdToFcName(fontName);
} else {
fname = wstrdup(fontName);
@@ -187,6 +174,10 @@ WMFont *WMCreateFont(WMScreen * scrPtr, const char *fontName)
pango_layout_set_font_description(layout, description);
font->layout = layout;
pango_font_description_free(description);
g_object_unref(context);
FcPatternDestroy(pattern);
#endif
assert(WMHashInsert(scrPtr->fontCache, font->name, font) == NULL);
@@ -210,6 +201,11 @@ void WMReleaseFont(WMFont * font)
font->refCount--;
if (font->refCount < 1) {
XftFontClose(font->screen->display, font->font);
#ifdef USE_PANGO
if (font->layout) {
g_object_unref(font->layout);
}
#endif
if (font->name) {
WMHashRemove(font->screen->fontCache, font->name);
wfree(font->name);
@@ -262,7 +258,11 @@ WMFont *WMSystemFontOfSize(WMScreen * scrPtr, int size)
WMFont *font;
char *fontSpec;
fontSpec = makeFontOfSize(WINGsConfiguration.systemFont, size, NULL);
fontSpec = makeFontOfSize(WINGsConfiguration.systemFont, size, DEFAULT_FONT);
if (!fontSpec) {
return NULL;
}
font = WMCreateFont(scrPtr, fontSpec);
@@ -280,7 +280,11 @@ WMFont *WMBoldSystemFontOfSize(WMScreen * scrPtr, int size)
WMFont *font;
char *fontSpec;
fontSpec = makeFontOfSize(WINGsConfiguration.boldSystemFont, size, NULL);
fontSpec = makeFontOfSize(WINGsConfiguration.boldSystemFont, size, DEFAULT_FONT);
if (!fontSpec) {
return NULL;
}
font = WMCreateFont(scrPtr, fontSpec);
@@ -336,7 +340,7 @@ void WMDrawString(WMScreen * scr, Drawable d, WMColor * color, WMFont * font, in
#ifdef USE_PANGO
previous_text = pango_layout_get_text(font->layout);
if ((previous_text == NULL) || (strcmp(text, previous_text) != 0))
if ((previous_text == NULL) || (strncmp(text, previous_text, length) != 0) || previous_text[length] != '\0')
pango_layout_set_text(font->layout, text, length);
pango_xft_render_layout(scr->xftdraw, &xftcolor, font->layout, x * PANGO_SCALE, y * PANGO_SCALE);
#else
@@ -374,7 +378,7 @@ WMDrawImageString(WMScreen * scr, Drawable d, WMColor * color, WMColor * backgro
#ifdef USE_PANGO
previous_text = pango_layout_get_text(font->layout);
if ((previous_text == NULL) || (strcmp(text, previous_text) != 0))
if ((previous_text == NULL) || (strncmp(text, previous_text, length) != 0) || previous_text[length] != '\0')
pango_layout_set_text(font->layout, text, length);
pango_xft_render_layout(scr->xftdraw, &textColor, font->layout, x * PANGO_SCALE, y * PANGO_SCALE);
#else
+498 -9
View File
@@ -1,5 +1,7 @@
#include "WINGsP.h"
#include <ctype.h>
#include <strings.h>
const char *WMListDidScrollNotification = "WMListDidScrollNotification";
const char *WMListSelectionDidChangeNotification = "WMListSelectionDidChangeNotification";
@@ -27,11 +29,15 @@ typedef struct W_List {
WMHandlerID *idleID; /* for updating the scroller after adding elements */
WMHandlerID *selectID; /* for selecting items in list while scrolling */
WMHandlerID *typeaheadID; /* for clearing typeahead buffer */
WMScroller *vScroller;
Pixmap doubleBuffer;
char *typeahead;
int typeaheadLen;
struct {
unsigned int allowMultipleSelection:1;
unsigned int allowEmptySelection:1;
@@ -48,6 +54,7 @@ typedef struct W_List {
#define DEFAULT_HEIGHT 150
#define SCROLL_DELAY 100
#define TYPEAHEAD_CLEAR_DELAY 700
static void destroyList(List * lPtr);
static void paintList(List * lPtr);
@@ -62,6 +69,8 @@ static void scrollBackwardSelecting(void *data);
static void vScrollCallBack(WMWidget * scroller, void *self);
static void toggleItemSelection(WMList * lPtr, int index);
static void jumpToFirstItemWithPrefix(WMList * lPtr, const char *prefix, int prefixLen);
static void typeaheadTimeout(void *data);
static void updateGeometry(WMList * lPtr);
static void didResizeList(W_ViewDelegate * self, WMView * view);
@@ -113,6 +122,9 @@ WMList *WMCreateList(WMWidget * parent)
W_Screen *scrPtr = W_VIEW(parent)->screen;
lPtr = wmalloc(sizeof(List));
lPtr->typeahead = NULL;
lPtr->typeaheadLen = 0;
lPtr->typeaheadID = NULL;
lPtr->widgetClass = WC_List;
@@ -129,7 +141,8 @@ WMList *WMCreateList(WMWidget * parent)
| ClientMessageMask, handleEvents, lPtr);
WMCreateEventHandler(lPtr->view, ButtonPressMask | ButtonReleaseMask
| EnterWindowMask | LeaveWindowMask | ButtonMotionMask, handleActionEvents, lPtr);
| EnterWindowMask | LeaveWindowMask | ButtonMotionMask
| KeyPressMask, handleActionEvents, lPtr);
lPtr->itemHeight = WMFontHeight(scrPtr->normalFont) + 1;
@@ -288,6 +301,14 @@ void WMClearList(WMList * lPtr)
WMDeleteTimerHandler(lPtr->selectID);
lPtr->selectID = NULL;
}
if (lPtr->typeaheadID) {
WMDeleteTimerHandler(lPtr->typeaheadID);
lPtr->typeaheadID = NULL;
}
if (lPtr->typeahead) {
lPtr->typeahead[0] = '\0';
lPtr->typeaheadLen = 0;
}
if (lPtr->view->flags.realized) {
updateScroller(lPtr);
}
@@ -495,15 +516,16 @@ static void paintItem(List * lPtr, int index)
static void paintList(List * lPtr)
{
W_Screen *scrPtr = lPtr->view->screen;
int i, lim;
int i, lim, itemCount;
if (!lPtr->view->flags.mapped)
return;
if (WMGetArrayItemCount(lPtr->items) > 0) {
if (lPtr->topItem + lPtr->fullFitLines + lPtr->flags.dontFitAll > WMGetArrayItemCount(lPtr->items)) {
itemCount = WMGetArrayItemCount(lPtr->items);
if (itemCount > 0) {
if (lPtr->topItem + lPtr->fullFitLines + lPtr->flags.dontFitAll > itemCount) {
lim = WMGetArrayItemCount(lPtr->items) - lPtr->topItem;
lim = itemCount - lPtr->topItem;
XClearArea(scrPtr->display, lPtr->view->window, 19,
2 + lim * lPtr->itemHeight, lPtr->view->size.width - 21,
lPtr->view->size.height - lim * lPtr->itemHeight - 3, False);
@@ -820,7 +842,7 @@ void WMSetListSelectionToRange(WMList * lPtr, WMRange range)
void WMSelectAllListItems(WMList * lPtr)
{
int i;
int i, itemCount;
WMListItem *item;
if (!lPtr->flags.allowMultipleSelection)
@@ -833,7 +855,8 @@ void WMSelectAllListItems(WMList * lPtr)
WMFreeArray(lPtr->selectedItems);
lPtr->selectedItems = WMCreateArrayWithArray(lPtr->items);
for (i = 0; i < WMGetArrayItemCount(lPtr->items); i++) {
itemCount = WMGetArrayItemCount(lPtr->items);
for (i = 0; i < itemCount; i++) {
item = WMGetFromArray(lPtr->items, i);
if (!item->selected) {
item->selected = 1;
@@ -859,10 +882,11 @@ void WMSelectAllListItems(WMList * lPtr)
*/
static void unselectAllListItems(WMList * lPtr, WMListItem * exceptThis)
{
int i;
int i, itemCount;
WMListItem *item;
for (i = 0; i < WMGetArrayItemCount(lPtr->items); i++) {
itemCount = WMGetArrayItemCount(lPtr->items);
for (i = 0; i < itemCount; i++) {
item = WMGetFromArray(lPtr->items, i);
if (item != exceptThis && item->selected) {
item->selected = 0;
@@ -920,6 +944,85 @@ static void toggleItemSelection(WMList * lPtr, int index)
}
}
static int findItemWithPrefix(List * lPtr, const char *prefix, int prefixLen)
{
if (prefixLen <= 0)
return -1;
int i, itemCount;
itemCount = WMGetArrayItemCount(lPtr->items);
for (i = 0; i < itemCount; i++) {
WMListItem *item = WMGetFromArray(lPtr->items, i);
if (!item || !item->text || item->text[0] == '\0')
continue;
if (strncasecmp(item->text, prefix, prefixLen) == 0)
return i;
}
return -1;
}
static void jumpToFirstItemWithPrefix(WMList * lPtr, const char *prefix, int prefixLen)
{
int index, oldTop, visibleCount;
index = findItemWithPrefix(lPtr, prefix, prefixLen);
if (index < 0)
return;
if (lPtr->flags.allowMultipleSelection) {
WMRange range;
range.position = index;
range.count = 1;
WMSetListSelectionToRange(lPtr, range);
} else {
WMSelectListItem(lPtr, index);
/* Trigger action callback */
if (lPtr->action)
(*lPtr->action) (lPtr, lPtr->clientData);
}
visibleCount = lPtr->fullFitLines + lPtr->flags.dontFitAll;
if (visibleCount < 1)
visibleCount = 1;
oldTop = lPtr->topItem;
if (index < lPtr->topItem) {
lPtr->topItem = index;
} else {
if (lPtr->flags.dontFitAll) {
if (lPtr->fullFitLines <= 0) {
lPtr->topItem = index;
} else {
int lastFullyVisible = lPtr->topItem + lPtr->fullFitLines - 1;
if (index > lastFullyVisible)
lPtr->topItem = index - lPtr->fullFitLines + 1;
}
} else if (index >= lPtr->topItem + visibleCount) {
lPtr->topItem = index - visibleCount + 1;
}
}
if (lPtr->topItem < 0)
lPtr->topItem = 0;
if (lPtr->view->flags.realized && lPtr->topItem != oldTop)
updateScroller(lPtr);
}
static void typeaheadTimeout(void *data)
{
List *lPtr = (List *) data;
lPtr->typeaheadID = NULL;
if (lPtr->typeahead) {
lPtr->typeahead[0] = '\0';
lPtr->typeaheadLen = 0;
}
}
static void handleActionEvents(XEvent * event, void *data)
{
List *lPtr = (List *) data;
@@ -959,6 +1062,7 @@ static void handleActionEvents(XEvent * event, void *data)
WMDeleteTimerHandler(lPtr->selectID);
lPtr->selectID = NULL;
}
WMSetFocusToWidget(lPtr);
break;
case LeaveNotify:
@@ -970,6 +1074,9 @@ static void handleActionEvents(XEvent * event, void *data)
lPtr->selectID = WMAddTimerHandler(SCROLL_DELAY, scrollBackwardSelecting, lPtr);
}
}
WMWidget *parentWidget = WMWidgetOfView(lPtr->view->parent);
if (parentWidget)
WMSetFocusToWidget(parentWidget);
break;
case ButtonPress:
@@ -1076,6 +1183,381 @@ static void handleActionEvents(XEvent * event, void *data)
prevItem = tmp;
}
break;
case KeyPress:
char buffer[16];
KeySym ksym;
Status status;
int len;
WMScreen *scr = lPtr->view->screen;
XWindowAttributes wattr;
if (event->xkey.state & (ControlMask | Mod1Mask))
break;
if (!(XGetWindowAttributes(scr->display, lPtr->view->window, &wattr) && wattr.map_state == IsViewable))
break;
len = W_LookupString(lPtr->view, &event->xkey, buffer, (int)sizeof(buffer) - 1, &ksym, &status);
if (len < 0)
break;
if (len > 0)
buffer[len] = '\0';
/* Handle navigation keys */
switch (ksym) {
case XK_Up: {
int newRow;
int itemCount = WMGetArrayItemCount(lPtr->items);
int cur = WMGetListSelectedItemRow(lPtr);
if (lPtr->flags.allowMultipleSelection && WMGetArrayItemCount(lPtr->selectedItems) > 0) {
WMListItem *lastSel = WMGetFromArray(lPtr->selectedItems, WMGetArrayItemCount(lPtr->selectedItems) - 1);
if (lastSel)
cur = WMGetFirstInArray(lPtr->items, lastSel);
}
if (cur == WLNotFound)
cur = lPtr->topItem;
newRow = cur - 1;
if (newRow < 0)
newRow = 0;
if (newRow != cur && itemCount > 0) {
if (lPtr->flags.allowMultipleSelection) {
if (event->xkey.state & ShiftMask) {
WMRange range;
int anchor = WMGetListSelectedItemRow(lPtr);
if (anchor == WLNotFound || WMGetArrayItemCount(lPtr->selectedItems) == 0) {
anchor = cur;
}
range.position = anchor;
if (newRow >= anchor)
range.count = newRow - anchor + 1;
else
range.count = newRow - anchor - 1;
WMSetListSelectionToRange(lPtr, range);
} else {
WMRange range = { .position = newRow, .count = 1 };
WMSetListSelectionToRange(lPtr, range);
lastClicked = newRow;
}
} else {
WMSelectListItem(lPtr, newRow);
lastClicked = newRow;
}
/* Ensure visibility */
if (newRow < lPtr->topItem) {
lPtr->topItem = newRow;
if (lPtr->view->flags.realized)
updateScroller(lPtr);
}
/* Trigger action callback */
if (lPtr->action)
(*lPtr->action) (lPtr, lPtr->clientData);
}
break;
}
case XK_Down: {
int newRow;
int itemCount = WMGetArrayItemCount(lPtr->items);
int cur = WMGetListSelectedItemRow(lPtr);
if (lPtr->flags.allowMultipleSelection && WMGetArrayItemCount(lPtr->selectedItems) > 0) {
WMListItem *lastSel = WMGetFromArray(lPtr->selectedItems, WMGetArrayItemCount(lPtr->selectedItems) - 1);
if (lastSel)
cur = WMGetFirstInArray(lPtr->items, lastSel);
}
if (cur == WLNotFound)
cur = lPtr->topItem;
newRow = cur + 1;
if (newRow >= itemCount)
newRow = itemCount - 1;
if (newRow != cur && itemCount > 0) {
if (lPtr->flags.allowMultipleSelection) {
if (event->xkey.state & ShiftMask) {
WMRange range;
int anchor = WMGetListSelectedItemRow(lPtr);
if (anchor == WLNotFound || WMGetArrayItemCount(lPtr->selectedItems) == 0) {
anchor = cur;
}
range.position = anchor;
if (newRow >= anchor)
range.count = newRow - anchor + 1;
else
range.count = newRow - anchor - 1;
WMSetListSelectionToRange(lPtr, range);
} else {
WMRange range = { .position = newRow, .count = 1 };
WMSetListSelectionToRange(lPtr, range);
lastClicked = newRow;
}
} else {
WMSelectListItem(lPtr, newRow);
lastClicked = newRow;
}
/* Ensure visibility */
if (newRow > (lPtr->topItem + lPtr->fullFitLines - 1)) {
lPtr->topItem = newRow - lPtr->fullFitLines + 1;
if (lPtr->topItem < 0)
lPtr->topItem = 0;
/* Ensure we don't scroll past the end */
if (lPtr->topItem + lPtr->fullFitLines > itemCount)
lPtr->topItem = itemCount - lPtr->fullFitLines;
if (lPtr->topItem < 0)
lPtr->topItem = 0;
if (lPtr->view->flags.realized)
updateScroller(lPtr);
}
/* Trigger action callback */
if (lPtr->action)
(*lPtr->action) (lPtr, lPtr->clientData);
}
break;
}
case XK_Page_Up: {
int newRow;
int page = lPtr->fullFitLines > 0 ? lPtr->fullFitLines : 1;
int cur = WMGetListSelectedItemRow(lPtr);
if (cur == WLNotFound)
cur = lPtr->topItem;
newRow = cur - page;
if (newRow < 0)
newRow = 0;
if (newRow != cur) {
if (lPtr->flags.allowMultipleSelection) {
if (event->xkey.state & ShiftMask) {
WMRange range;
int anchor = WMGetListSelectedItemRow(lPtr);
if (anchor == WLNotFound || WMGetArrayItemCount(lPtr->selectedItems) == 0) {
anchor = cur;
}
range.position = anchor;
if (newRow >= anchor)
range.count = newRow - anchor + 1;
else
range.count = newRow - anchor - 1;
WMSetListSelectionToRange(lPtr, range);
} else {
WMRange range = { .position = newRow, .count = 1 };
WMSetListSelectionToRange(lPtr, range);
lastClicked = newRow;
}
} else {
WMSelectListItem(lPtr, newRow);
lastClicked = newRow;
}
if (newRow < lPtr->topItem) {
lPtr->topItem = newRow;
if (lPtr->view->flags.realized)
updateScroller(lPtr);
}
/* Trigger action callback */
if (lPtr->action)
(*lPtr->action) (lPtr, lPtr->clientData);
}
break;
}
case XK_Page_Down: {
int newRow;
int page = lPtr->fullFitLines > 0 ? lPtr->fullFitLines : 1;
int itemCount = WMGetArrayItemCount(lPtr->items);
int cur = WMGetListSelectedItemRow(lPtr);
if (cur == WLNotFound)
cur = lPtr->topItem;
newRow = cur + page;
if (newRow >= itemCount)
newRow = itemCount - 1;
if (newRow != cur && itemCount > 0) {
if (lPtr->flags.allowMultipleSelection) {
if (event->xkey.state & ShiftMask) {
WMRange range;
int anchor = WMGetListSelectedItemRow(lPtr);
if (anchor == WLNotFound || WMGetArrayItemCount(lPtr->selectedItems) == 0) {
anchor = cur;
}
range.position = anchor;
if (newRow >= anchor)
range.count = newRow - anchor + 1;
else
range.count = newRow - anchor - 1;
WMSetListSelectionToRange(lPtr, range);
} else {
WMRange range = { .position = newRow, .count = 1 };
WMSetListSelectionToRange(lPtr, range);
lastClicked = newRow;
}
} else {
WMSelectListItem(lPtr, newRow);
lastClicked = newRow;
}
/* Ensure visibility */
if (newRow > (lPtr->topItem + lPtr->fullFitLines - 1)) {
lPtr->topItem = newRow - lPtr->fullFitLines + 1;
if (lPtr->topItem < 0)
lPtr->topItem = 0;
/* Ensure we don't scroll past the end */
if (lPtr->topItem + lPtr->fullFitLines > itemCount)
lPtr->topItem = itemCount - lPtr->fullFitLines;
if (lPtr->topItem < 0)
lPtr->topItem = 0;
if (lPtr->view->flags.realized)
updateScroller(lPtr);
}
/* Trigger action callback */
if (lPtr->action)
(*lPtr->action) (lPtr, lPtr->clientData);
}
break;
}
case XK_Home: {
int itemCount = WMGetArrayItemCount(lPtr->items);
if (itemCount > 0) {
int newRow = 0;
if (lPtr->flags.allowMultipleSelection) {
if (event->xkey.state & ShiftMask) {
WMRange range;
int anchor = WMGetListSelectedItemRow(lPtr);
if (anchor == WLNotFound || WMGetArrayItemCount(lPtr->selectedItems) == 0) {
anchor = lPtr->topItem;
}
range.position = anchor;
if (newRow >= anchor)
range.count = newRow - anchor + 1;
else
range.count = newRow - anchor - 1;
WMSetListSelectionToRange(lPtr, range);
} else {
WMRange range = { .position = newRow, .count = 1 };
WMSetListSelectionToRange(lPtr, range);
lastClicked = newRow;
}
} else {
WMSelectListItem(lPtr, newRow);
lastClicked = newRow;
}
if (newRow < lPtr->topItem) {
lPtr->topItem = newRow;
if (lPtr->view->flags.realized)
updateScroller(lPtr);
}
/* Trigger action callback */
if (lPtr->action)
(*lPtr->action) (lPtr, lPtr->clientData);
}
break;
}
case XK_End: {
int itemCount = WMGetArrayItemCount(lPtr->items);
if (itemCount > 0) {
int newRow = itemCount - 1;
if (lPtr->flags.allowMultipleSelection) {
if (event->xkey.state & ShiftMask) {
WMRange range;
int anchor = WMGetListSelectedItemRow(lPtr);
if (anchor == WLNotFound || WMGetArrayItemCount(lPtr->selectedItems) == 0) {
anchor = lPtr->topItem;
}
range.position = anchor;
if (newRow >= anchor)
range.count = newRow - anchor + 1;
else
range.count = newRow - anchor - 1;
WMSetListSelectionToRange(lPtr, range);
} else {
WMRange range = { .position = newRow, .count = 1 };
WMSetListSelectionToRange(lPtr, range);
lastClicked = newRow;
}
} else {
WMSelectListItem(lPtr, newRow);
lastClicked = newRow;
}
/* Ensure the last item is fully visible */
lPtr->topItem = itemCount - lPtr->fullFitLines;
if (lPtr->topItem < 0)
lPtr->topItem = 0;
if (lPtr->view->flags.realized)
updateScroller(lPtr);
/* Trigger action callback */
if (lPtr->action)
(*lPtr->action) (lPtr, lPtr->clientData);
}
break;
}
default:
break;
}
/* If we handled a navigation keysym, avoid falling through to typeahead logic */
if (ksym == XK_Up || ksym == XK_Down || ksym == XK_Page_Up || ksym == XK_Page_Down || ksym == XK_Home || ksym == XK_End)
break;
if (len <= 0)
break;
buffer[len] = '\0';
if (ksym == XK_Escape) {
if (lPtr->typeaheadID) {
WMDeleteTimerHandler(lPtr->typeaheadID);
lPtr->typeaheadID = NULL;
}
if (lPtr->typeahead) {
lPtr->typeahead[0] = '\0';
lPtr->typeaheadLen = 0;
}
break;
}
if (ksym == XK_BackSpace) {
if (lPtr->typeaheadLen > 0 && lPtr->typeahead) {
lPtr->typeaheadLen--;
lPtr->typeahead[lPtr->typeaheadLen] = '\0';
}
} else if (len == 1 && isalnum((unsigned char)buffer[0])) {
if (!lPtr->typeahead) {
lPtr->typeahead = wmalloc(2);
lPtr->typeaheadLen = 0;
}
lPtr->typeahead = wrealloc(lPtr->typeahead, lPtr->typeaheadLen + 2);
lPtr->typeahead[lPtr->typeaheadLen] = buffer[0];
lPtr->typeaheadLen++;
lPtr->typeahead[lPtr->typeaheadLen] = '\0';
} else {
break;
}
if (lPtr->typeaheadLen > 0)
jumpToFirstItemWithPrefix(lPtr, lPtr->typeahead, lPtr->typeaheadLen);
if (lPtr->typeaheadID) {
WMDeleteTimerHandler(lPtr->typeaheadID);
lPtr->typeaheadID = NULL;
}
if (lPtr->typeaheadLen > 0)
lPtr->typeaheadID = WMAddTimerHandler(TYPEAHEAD_CLEAR_DELAY, typeaheadTimeout, lPtr);
break;
}
if (lPtr->topItem != topItem)
WMPostNotificationName(WMListDidScrollNotification, lPtr, NULL);
@@ -1123,6 +1605,10 @@ static void destroyList(List * lPtr)
WMDeleteTimerHandler(lPtr->selectID);
lPtr->selectID = NULL;
if (lPtr->typeaheadID)
WMDeleteTimerHandler(lPtr->typeaheadID);
lPtr->typeaheadID = NULL;
if (lPtr->selectedItems)
WMFreeArray(lPtr->selectedItems);
@@ -1132,6 +1618,9 @@ static void destroyList(List * lPtr)
if (lPtr->doubleBuffer)
XFreePixmap(lPtr->view->screen->display, lPtr->doubleBuffer);
if (lPtr->typeahead)
wfree(lPtr->typeahead);
WMRemoveNotificationObserver(lPtr);
wfree(lPtr);
+50
View File
@@ -252,6 +252,56 @@ WMMenuItem *WMGetPopUpButtonMenuItem(WMPopUpButton * bPtr, int index)
return WMGetFromArray(bPtr->items, index);
}
int WMSelectPopUpButtonPreviousItem(WMPopUpButton * bPtr)
{
int testIndex;
CHECK_CLASS(bPtr, WC_PopUpButton);
if (bPtr->flags.pullsDown || bPtr->selectedItemIndex < 0)
return -1;
testIndex = bPtr->selectedItemIndex - 1;
while (testIndex >= 0 && !WMGetPopUpButtonItemEnabled(bPtr, testIndex))
testIndex--;
if (testIndex != -1) {
WMSetPopUpButtonSelectedItem(bPtr, testIndex);
if (bPtr->action)
(*bPtr->action) (bPtr, bPtr->clientData);
return testIndex;
}
return -1;
}
int WMSelectPopUpButtonNextItem(WMPopUpButton * bPtr)
{
int itemCount;
int testIndex;
CHECK_CLASS(bPtr, WC_PopUpButton);
if (bPtr->flags.pullsDown || bPtr->selectedItemIndex < 0)
return -1;
itemCount = WMGetArrayItemCount(bPtr->items);
testIndex = bPtr->selectedItemIndex + 1;
while (testIndex < itemCount && !WMGetPopUpButtonItemEnabled(bPtr, testIndex))
testIndex++;
if (testIndex != itemCount) {
WMSetPopUpButtonSelectedItem(bPtr, testIndex);
if (bPtr->action)
(*bPtr->action) (bPtr, bPtr->clientData);
return testIndex;
}
return -1;
}
static void paintPopUpButton(PopUpButton * bPtr)
{
W_Screen *scr = bPtr->view->screen;
+16 -16
View File
@@ -219,9 +219,9 @@ static char *default_bullet[] = {
};
/* These id are used when sharing the selected text between applications */
static Atom XA_Targets = None;
static Atom XA_Format_Text = None;
static Atom XA_Format_Compound_Text = None;
static Atom XA_TARGETS = None;
static Atom XA_TEXT = None;
static Atom XA_COMPOUND_TEXT = None;
static void handleEvents(XEvent * event, void *data);
static void layOutDocument(Text * tPtr);
@@ -2050,7 +2050,7 @@ static WMData *requestHandler(WMView * view, Atom selection, Atom target, void *
(void) selection;
(void) cdata;
if (target == XA_STRING || target == XA_Format_Text || target == XA_Format_Compound_Text) {
if (target == XA_STRING || target == XA_TEXT || target == XA_COMPOUND_TEXT) {
char *text = WMGetTextSelectedStream(tPtr);
if (text) {
@@ -2063,18 +2063,18 @@ static WMData *requestHandler(WMView * view, Atom selection, Atom target, void *
} else
printf("didn't get it\n");
if (target == XA_Targets) {
Atom array[4];
if (target == XA_TARGETS) {
Atom supported_type[4];
array[0] = XA_Targets;
array[1] = XA_STRING;
array[2] = XA_Format_Text;
array[3] = XA_Format_Compound_Text;
supported_type[0] = XA_TARGETS;
supported_type[1] = XA_STRING;
supported_type[2] = XA_TEXT;
supported_type[3] = XA_COMPOUND_TEXT;
data = WMCreateDataWithBytes(&array, sizeof(array));
data = WMCreateDataWithBytes(supported_type, sizeof(supported_type));
WMSetDataFormat(data, 32);
*type = target;
*type = XA_ATOM;
return data;
}
@@ -2977,15 +2977,15 @@ WMText *WMCreateTextForDocumentType(WMWidget * parent, WMAction * parser, WMActi
dpy = tPtr->view->screen->display;
scr = tPtr->view->screen;
if (XA_Targets == None) {
if (XA_TARGETS == None) {
/*
* Because the X protocol guaranties that the value will never change in
* the lifespan of the server, we query the values only the first time a
* widget is created
*/
XA_Targets = XInternAtom(dpy, "TARGETS", False);
XA_Format_Text = XInternAtom(dpy, "TEXT", False);
XA_Format_Compound_Text = XInternAtom(dpy, "COMPOUND_TEXT", False);
XA_TARGETS = XInternAtom(dpy, "TARGETS", False);
XA_TEXT = XInternAtom(dpy, "TEXT", False);
XA_COMPOUND_TEXT = XInternAtom(dpy, "COMPOUND_TEXT", False);
}
tPtr->view->self = tPtr;
+89 -59
View File
@@ -239,9 +239,9 @@ static WMData *requestHandler(WMView * view, Atom selection, Atom target, void *
TextField *tPtr = view->self;
int count;
Display *dpy = tPtr->view->screen->display;
Atom _TARGETS;
Atom TEXT = XInternAtom(dpy, "TEXT", False);
Atom COMPOUND_TEXT = XInternAtom(dpy, "COMPOUND_TEXT", False);
Atom XA_TARGETS;
Atom XA_TEXT = XInternAtom(dpy, "TEXT", False);
Atom XA_COMPOUND_TEXT = XInternAtom(dpy, "COMPOUND_TEXT", False);
WMData *data;
/* Parameter not used, but tell the compiler that it is ok */
@@ -251,7 +251,7 @@ static WMData *requestHandler(WMView * view, Atom selection, Atom target, void *
count = tPtr->selection.count < 0
? tPtr->selection.position + tPtr->selection.count : tPtr->selection.position;
if (target == XA_STRING || target == TEXT || target == COMPOUND_TEXT) {
if (target == XA_STRING || target == XA_TEXT || target == XA_COMPOUND_TEXT) {
data = WMCreateDataWithBytes(&(tPtr->text[count]), abs(tPtr->selection.count));
WMSetDataFormat(data, 8);
@@ -260,19 +260,19 @@ static WMData *requestHandler(WMView * view, Atom selection, Atom target, void *
return data;
}
_TARGETS = XInternAtom(dpy, "TARGETS", False);
if (target == _TARGETS) {
XA_TARGETS = XInternAtom(dpy, "TARGETS", False);
if (target == XA_TARGETS) {
Atom supported_type[4];
supported_type[0] = _TARGETS;
supported_type[0] = XA_TARGETS;
supported_type[1] = XA_STRING;
supported_type[2] = TEXT;
supported_type[3] = COMPOUND_TEXT;
supported_type[2] = XA_TEXT;
supported_type[3] = XA_COMPOUND_TEXT;
data = WMCreateDataWithBytes(supported_type, sizeof(supported_type));
WMSetDataFormat(data, 32);
*type = target;
*type = XA_ATOM;
return data;
}
@@ -446,6 +446,13 @@ void WMDeleteTextFieldRange(WMTextField * tPtr, WMRange range)
decrToFit(tPtr);
/* Ensure cursor is visible after deletion */
if (tPtr->cursorPosition < tPtr->viewPosition) {
tPtr->viewPosition = tPtr->cursorPosition;
} else {
incrToFit2(tPtr);
}
paintTextField(tPtr);
}
@@ -490,11 +497,6 @@ void WMSetTextFieldAlignment(WMTextField * tPtr, WMAlignment alignment)
tPtr->flags.alignment = alignment;
if (alignment != WALeft) {
wwarning(_("only left alignment is supported in textfields"));
return;
}
if (tPtr->view->flags.realized) {
paintTextField(tPtr);
}
@@ -846,8 +848,8 @@ static void paintTextField(TextField * tPtr)
count = tPtr->viewPosition;
}
rx = tPtr->offsetWidth + 1 + WMWidthOfString(tPtr->font, text, count)
- WMWidthOfString(tPtr->font, text, tPtr->viewPosition);
rx = tx + WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]),
count - tPtr->viewPosition);
WMDrawImageString(screen, drawbuffer, color, screen->gray,
tPtr->font, rx, ty, &(text[count]), count2);
@@ -1029,7 +1031,6 @@ static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
case XK_Left:
if (tPtr->cursorPosition > 0) {
int i;
paintCursor(tPtr);
i = tPtr->cursorPosition;
i += oneUTF8CharBackward(&tPtr->text[i], i);
@@ -1045,9 +1046,8 @@ static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
if (tPtr->cursorPosition < tPtr->viewPosition) {
tPtr->viewPosition = tPtr->cursorPosition;
refresh = 1;
} else
paintCursor(tPtr);
}
refresh = 1;
}
if (shifted)
cancelSelection = 0;
@@ -1070,7 +1070,6 @@ static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
case XK_Right:
if (tPtr->cursorPosition < tPtr->textLen) {
int i;
paintCursor(tPtr);
i = tPtr->cursorPosition;
if (controled) {
@@ -1083,10 +1082,8 @@ static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
}
tPtr->cursorPosition = i;
refresh = incrToFit2(tPtr);
if (!refresh)
paintCursor(tPtr);
incrToFit2(tPtr);
refresh = 1;
}
if (shifted)
cancelSelection = 0;
@@ -1109,13 +1106,11 @@ static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
case XK_Home:
if (!controled) {
if (tPtr->cursorPosition > 0) {
paintCursor(tPtr);
tPtr->cursorPosition = 0;
if (tPtr->viewPosition > 0) {
tPtr->viewPosition = 0;
refresh = 1;
} else
paintCursor(tPtr);
}
refresh = 1;
}
if (shifted)
cancelSelection = 0;
@@ -1138,14 +1133,11 @@ static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
case XK_End:
if (!controled) {
if (tPtr->cursorPosition < tPtr->textLen) {
paintCursor(tPtr);
tPtr->cursorPosition = tPtr->textLen;
tPtr->viewPosition = 0;
refresh = incrToFit(tPtr);
if (!refresh)
paintCursor(tPtr);
incrToFit(tPtr);
refresh = 1;
}
if (shifted)
cancelSelection = 0;
@@ -1408,7 +1400,25 @@ static void handleTextFieldActionEvents(XEvent * event, void *data)
tPtr->viewPosition);
}
tPtr->cursorPosition = pointToCursorPosition(tPtr, event->xmotion.x);
if (tPtr->flags.alignment == WARight) {
int textWidth = WMWidthOfString(tPtr->font, tPtr->text, tPtr->textLen);
if (textWidth < tPtr->usableWidth) {
tPtr->cursorPosition = pointToCursorPosition(tPtr,
event->xmotion.x - tPtr->usableWidth + textWidth);
} else {
tPtr->cursorPosition = pointToCursorPosition(tPtr, event->xmotion.x);
}
} else if (tPtr->flags.alignment == WACenter) {
int textWidth = WMWidthOfString(tPtr->font, tPtr->text, tPtr->textLen);
if (textWidth < tPtr->usableWidth) {
tPtr->cursorPosition = pointToCursorPosition(tPtr,
event->xmotion.x - (tPtr->usableWidth - textWidth) / 2);
} else {
tPtr->cursorPosition = pointToCursorPosition(tPtr, event->xmotion.x);
}
} else {
tPtr->cursorPosition = pointToCursorPosition(tPtr, event->xmotion.x);
}
/* Do not allow text selection in secure textfields */
if (tPtr->flags.secure) {
@@ -1441,17 +1451,35 @@ static void handleTextFieldActionEvents(XEvent * event, void *data)
if (tPtr->flags.enabled && !tPtr->flags.focused) {
WMSetFocusToWidget(tPtr);
}
if (textWidth < tPtr->usableWidth) {
tPtr->cursorPosition = pointToCursorPosition(tPtr,
event->xbutton.x - tPtr->usableWidth
+ textWidth);
} else
tPtr->cursorPosition = pointToCursorPosition(tPtr, event->xbutton.x);
if (tPtr->flags.focused) {
tPtr->selection.position = tPtr->cursorPosition;
tPtr->selection.count = 0;
}
paintTextField(tPtr);
break;
case WACenter:
textWidth = WMWidthOfString(tPtr->font, tPtr->text, tPtr->textLen);
if (tPtr->flags.enabled && !tPtr->flags.focused) {
WMSetFocusToWidget(tPtr);
}
if (textWidth < tPtr->usableWidth) {
tPtr->cursorPosition = pointToCursorPosition(tPtr,
event->xbutton.x - tPtr->usableWidth
+ textWidth);
} else
event->xbutton.x - (tPtr->usableWidth - textWidth) / 2);
} else {
tPtr->cursorPosition = pointToCursorPosition(tPtr, event->xbutton.x);
}
if (tPtr->flags.focused) {
tPtr->selection.position = tPtr->cursorPosition;
tPtr->selection.count = 0;
}
paintTextField(tPtr);
break;
@@ -1465,29 +1493,31 @@ static void handleTextFieldActionEvents(XEvent * event, void *data)
tPtr->selection.count = 0;
paintTextField(tPtr);
}
if (event->xbutton.button == Button2 && tPtr->flags.enabled) {
char *text;
int n;
if (!WMRequestSelection(tPtr->view, XA_PRIMARY, XA_STRING,
event->xbutton.time, pasteText, NULL)) {
text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
if (text) {
text[n] = 0;
WMInsertTextFieldText(tPtr, text, tPtr->cursorPosition);
XFree(text);
NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
(void *)WMInsertTextEvent);
}
} else {
tPtr->flags.waitingSelection = 1;
}
}
break;
default:
break;
}
if (event->xbutton.button == Button2 && tPtr->flags.enabled) {
char *text;
int n;
if (!WMRequestSelection(tPtr->view, XA_PRIMARY, XA_STRING,
event->xbutton.time, pasteText, NULL)) {
text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
if (text) {
text[n] = 0;
WMInsertTextFieldText(tPtr, text, tPtr->cursorPosition);
XFree(text);
NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
(void *)WMInsertTextEvent);
}
} else {
tPtr->flags.waitingSelection = 1;
}
}
break;
case ButtonRelease:
+134 -2
View File
@@ -4,7 +4,7 @@
*
* Copyright (c) 2014-2023 Window Maker Team
* Copyright (c) 1998-2003 Alfredo K. Kojima
* Copyright (c) 2009-2023 Window Maker Team
* Copyright (c) 2009-2026 Window Maker Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -72,9 +72,15 @@ static struct expert_option {
{ N_("Cycle windows only on the active head."),
/* default: */ False, OPTION_WMAKER, "CycleActiveHeadOnly" },
{ N_("Cycle all windows from all workspaces."),
/* default: */ False, OPTION_WMAKER, "CycleAllWorkspaces" },
{ N_("Ignore minimized windows when cycling."),
/* default: */ False, OPTION_WMAKER, "CycleIgnoreMinimized" },
{ N_("Show app icons in window list."),
/* default: */ False, OPTION_WMAKER, "WindowListAppIcons" },
{ N_("Show switch panel when cycling windows."),
/* default: */ True, OPTION_WMAKER_ARRAY, "SwitchPanelImages" },
@@ -141,6 +147,8 @@ typedef struct _Panel {
WMButton *swi[wlengthof_nocheck(expert_options)];
WMTextField *textfield[wlengthof_nocheck(expert_options)];
WMScrollView *sv;
WMWidget *frame;
} _Panel;
@@ -161,6 +169,124 @@ static void changeIntTextfield(void *data, int delta)
WMSetTextFieldText(textfield, buffer);
}
static void scrollViewWheelHandler(XEvent *event, void *data)
{
_Panel *panel = (_Panel *) data;
int amount, viewH, contentH, newY, maxY;
WMRect rect;
WMPoint pt;
if (!panel || !panel->sv || !panel->frame)
return;
if (event->type != ButtonPress)
return;
if (event->xbutton.button != WINGsConfiguration.mouseWheelUp &&
event->xbutton.button != WINGsConfiguration.mouseWheelDown)
return;
rect = WMGetScrollViewVisibleRect(panel->sv);
viewH = rect.size.height;
contentH = WMWidgetHeight(panel->frame);
if (event->xbutton.state & ControlMask) {
amount = viewH; /* page */
} else if (event->xbutton.state & ShiftMask) {
amount = 1; /* line */
} else {
amount = viewH / 3; /* default */
if (amount == 0)
amount = 1;
}
if (event->xbutton.button == WINGsConfiguration.mouseWheelUp)
amount = -amount;
newY = rect.pos.y + amount;
maxY = contentH - viewH;
if (maxY < 0)
maxY = 0;
if (newY < 0)
newY = 0;
if (newY > maxY)
newY = maxY;
pt.x = rect.pos.x;
pt.y = newY;
WMScrollViewScrollPoint(panel->sv, pt);
}
static void scrollViewRealizeObserver(void *self, WMNotification *not)
{
(void) not;
_Panel *panel = (_Panel *) self;
Display *dpy = NULL;
Window viewport_win = 0;
WMView *frameView;
if (!panel || !panel->frame)
return;
frameView = WMWidgetView(panel->frame);
if (frameView && frameView->parent) {
dpy = frameView->screen->display;
viewport_win = frameView->parent->window;
}
/* fallback: use the scrollview's view window if parent viewport not available */
if (!viewport_win && panel->sv) {
WMView *svView = WMWidgetView(panel->sv);
if (svView && svView->screen) {
dpy = svView->screen->display;
viewport_win = svView->window;
}
}
if (!dpy || viewport_win == 0)
return;
XGrabButton(dpy, WINGsConfiguration.mouseWheelUp, AnyModifier, viewport_win,
True, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
XGrabButton(dpy, WINGsConfiguration.mouseWheelDown, AnyModifier, viewport_win,
True, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
}
static void scrollViewPrepareForClose(Panel *p)
{
_Panel *panel = (_Panel *) p;
Display *dpy = NULL;
Window viewport_win = 0;
if (!panel)
return;
if (panel->frame) {
WMView *frameView = WMWidgetView(panel->frame);
if (frameView && frameView->parent) {
dpy = frameView->screen->display;
viewport_win = frameView->parent->window;
}
}
if (!viewport_win && panel->sv) {
WMView *svView = WMWidgetView(panel->sv);
if (svView && svView->screen) {
dpy = svView->screen->display;
viewport_win = svView->window;
}
}
if (dpy && viewport_win != 0) {
XUngrabButton(dpy, WINGsConfiguration.mouseWheelUp, AnyModifier, viewport_win);
XUngrabButton(dpy, WINGsConfiguration.mouseWheelDown, AnyModifier, viewport_win);
}
WMRemoveNotificationObserver(panel);
}
static void downButtonCallback(WMWidget *self, void *data)
{
(void) self;
@@ -284,7 +410,7 @@ static void createPanel(Panel *p)
default:
#ifdef DEBUG
wwarning("export_options[%d].class = %d, this should not happen\n",
wwarning("expert_options[%d].class = %d, this should not happen\n",
i, expert_options[i].class);
#endif
state = expert_options[i].def_state;
@@ -296,6 +422,11 @@ static void createPanel(Panel *p)
WMMapSubwidgets(panel->box);
WMSetScrollViewContentView(sv, WMWidgetView(f));
/* keep references for the wheel handler and register it */
panel->sv = sv;
panel->frame = f;
WMCreateEventHandler(WMWidgetView(sv), ButtonPressMask, scrollViewWheelHandler, panel);
WMAddNotificationObserver(scrollViewRealizeObserver, panel, WMViewRealizedNotification, WMWidgetView(sv));
WMRealizeWidget(panel->box);
}
@@ -356,6 +487,7 @@ Panel *InitExpert(WMWidget *parent)
panel->callbacks.createWidgets = createPanel;
panel->callbacks.updateDomain = storeDefaults;
panel->callbacks.prepareForClose = scrollViewPrepareForClose;
AddSection(panel, ICON_FILE);
+1 -1
View File
@@ -257,7 +257,7 @@ static void createPanel(Panel * p)
WMMoveWidget(panel->hcdescF, 15, 130);
panel->hcdescL = WMCreateLabel(panel->hcdescF);
WMResizeWidget(panel->hcdescL, 200, 60);
WMResizeWidget(panel->hcdescL, 200, 70);
WMMoveWidget(panel->hcdescL, 15, 10);
WMSetLabelText(panel->hcdescL,
_("Instructions:\n\n"
+3 -3
View File
@@ -284,14 +284,14 @@ static void createPanel(Panel * p)
/***************** Options ****************/
panel->optF = WMCreateFrame(panel->box);
WMResizeWidget(panel->optF, 255, 94);
WMResizeWidget(panel->optF, 255, 96);
WMMoveWidget(panel->optF, 15, 125);
WMSetFrameTitle(panel->optF, _("AppIcon bouncing"));
for (i = 0; i < wlengthof(appicon_bouncing); i++) {
panel->bounceB[i] = WMCreateSwitchButton(panel->optF);
WMResizeWidget(panel->bounceB[i], 237, 26);
WMMoveWidget(panel->bounceB[i], 9, 14 + i * 25);
WMMoveWidget(panel->bounceB[i], 9, 16 + i * 26);
WMSetButtonText(panel->bounceB[i], _(appicon_bouncing[i].label));
if (appicon_bouncing[i].default_value)
@@ -306,7 +306,7 @@ static void createPanel(Panel * p)
/***************** Workspace border ****************/
panel->borderF = WMCreateFrame(panel->box);
WMResizeWidget(panel->borderF, 220, 75);
WMResizeWidget(panel->borderF, 220, 77);
WMMoveWidget(panel->borderF, 285, 144);
WMSetFrameTitle(panel->borderF, _("Workspace border"));
+1 -1
View File
@@ -26,4 +26,4 @@ et.po Estonian Ivar Smolin <okul@linux.ee>
ca.po Catalan Ernest Adrogué <eadrogue@gmx.net>
nl.po Dutch Alwin <translations@ziggo.nl>
fy.po Frisian Alwin <translations@ziggo.nl>
sr.po Serbian Strahinya Radich <contact@strahinja.org>
sr.po Serbian Strahinya Radich <sr@strahinja.org>
+2 -2
View File
@@ -3,7 +3,7 @@
# 1998-2006 Dan Pascu
# 2013-2020 Window Maker Developers Team
# This file is distributed under the same license as the Window Maker package.
# Strahinya Radich (Страхиња Радић) <contact@strahinja.org>, 2023.
# Strahinya Radich (Страхиња Радић) <sr@strahinja.org>, 2023.
#
msgid ""
msgstr ""
@@ -11,7 +11,7 @@ msgstr ""
"Report-Msgid-Bugs-To: wmaker-dev@googlegroups.com\n"
"POT-Creation-Date: 2023-11-02 19:25+0100\n"
"PO-Revision-Date: 2023-11-02 19:48+0100\n"
"Last-Translator: Страхиња Радић <contact@strahinja.org>\n"
"Last-Translator: Страхиња Радић <sr@strahinja.org>\n"
"Language-Team: Serbian <sr@li.org>\n"
"Language: sr\n"
"MIME-Version: 1.0\n"
+3 -1
View File
@@ -233,7 +233,7 @@
ScreenSwitchKey = None;
RunKey = None;
ExitKey = None;
ScreenCaptureKey = Print;
ScreenCaptureKey = None;
WindowCaptureKey = None;
PartialCaptureKey = None;
NormalCursor = (builtin, left_ptr);
@@ -252,5 +252,7 @@
SelectCursor = (builtin, cross);
DialogHistoryLines = 500;
CycleActiveHeadOnly = NO;
CycleAllWorkspaces = NO;
CycleIgnoreMinimized = NO;
WindowListAppIcons = NO;
}
+2 -2
View File
@@ -64,7 +64,7 @@ menu.bg Bulgarian Slavei Karadjov <slaff@exco.net>
menu.sk Slovak judas@hell <tomka@oalevice.sk>
menu.nl Dutch Alwin <translations@ziggo.nl>
menu.fy Frisian Alwin <translations@ziggo.nl>
menu.sr Serbian Strahinya Radich <contact@strahinja.org>
menu.sr Serbian Strahinya Radich <sr@strahinja.org>
@@ -81,5 +81,5 @@ plmenu.sk Slovak judas@hell <tomka@oalevice.sk>
plmenu.ja Japanese Seiichi SATO <sato@cvs-net.co.jp>
plmenu.nl Dutch Alwin <translations@ziggo.nl>
plmenu.fy Frisian Alwin <translations@ziggo.nl>
plmenu.sr Serbian Strahinya Radich <contact@strahinja.org>
plmenu.sr Serbian Strahinya Radich <sr@strahinja.org>
-1
View File
@@ -4,4 +4,3 @@ Comment=This session logs you into Window Maker
Exec=wmaker
TryExec=wmaker
DesktopNames=WindowMaker
X-GDM-SessionRegisters=true
+50 -4
View File
@@ -71,16 +71,16 @@ dnl 6. If any interfaces have been removed or changed since the last
dnl public release, then set age to 0.
dnl
dnl libwraster
WRASTER_CURRENT=7
WRASTER_CURRENT=8
WRASTER_REVISION=0
WRASTER_AGE=1
WRASTER_AGE=2
WRASTER_VERSION=$WRASTER_CURRENT:$WRASTER_REVISION:$WRASTER_AGE
AC_SUBST(WRASTER_VERSION)
dnl
dnl libWINGs
WINGS_CURRENT=5
WINGS_CURRENT=6
WINGS_REVISION=0
WINGS_AGE=2
WINGS_AGE=3
WINGS_VERSION=$WINGS_CURRENT:$WINGS_REVISION:$WINGS_AGE
AC_SUBST(WINGS_VERSION)
dnl
@@ -130,6 +130,20 @@ AS_IF([test "x$debug" = "xyes"],
])
dnl Native CPU Optimizations Option
dnl =================
m4_divert_push([INIT_PREPARE])dnl
AC_ARG_ENABLE([native],
[AS_HELP_STRING([--enable-native], [enable native option, @<:@default=no@:>@])],
[AS_CASE(["$enableval"],
[yes], [native=yes],
[no], [native=no],
[AC_MSG_ERROR([bad value $enableval for --enable-native])] )],
[native=no])
m4_divert_pop([INIT_PREPARE])dnl
AS_IF([test "x$native" = "xyes"],
[AX_CFLAGS_GCC_OPTION([-march=native])])
AX_CFLAGS_GCC_OPTION([-Wall])
AX_CFLAGS_GCC_OPTION([-Wextra -Wno-sign-compare])
dnl
@@ -766,6 +780,19 @@ m4_divert_pop([INIT_PREPARE])dnl
WM_IMGFMT_CHECK_JPEG
dnl JXL Support
dnl ============
m4_divert_push([INIT_PREPARE])dnl
AC_ARG_ENABLE([jxl],
[AS_HELP_STRING([--disable-jxl], [disable JXL support through libjxl])],
[AS_CASE(["$enableval"],
[yes|no], [],
[AC_MSG_ERROR([bad value $enableval for --enable-jxl])] )],
[enable_jxl=auto])
m4_divert_pop([INIT_PREPARE])dnl
WM_IMGFMT_CHECK_JXL
dnl GIF Support
dnl ============
m4_divert_push([INIT_PREPARE])dnl
@@ -844,6 +871,22 @@ AM_CONDITIONAL([ICON_EXT_XPM], [test "x$ICONEXT" = "xxpm"])
AM_CONDITIONAL([ICON_EXT_TIFF], [test "x$ICONEXT" = "xtiff"])
dnl Archive Support for wmiv
dnl =================================
dnl Check for libarchive (comprehensive archive support)
AC_CHECK_LIB([archive], [archive_read_new], [
AC_CHECK_HEADER([archive.h], [
AC_DEFINE([HAVE_LIBARCHIVE], [1], [Define if libarchive is available])
LIBARCHIVE_LIBS="-larchive"
], [
AC_MSG_WARN([libarchive header not found, archive support disabled])
])
], [
AC_MSG_WARN([libarchive not found, archive support disabled])
])
AC_SUBST([LIBARCHIVE_LIBS])
dnl ==============================================
dnl End of Graphic Format Libraries
dnl ==============================================
@@ -1008,6 +1051,9 @@ echo "Supported graphic format libraries :$supported_gfx"
echo "Unsupported features :$unsupported"
echo "Pango text layout support in WINGs : $pango"
echo "Translated languages to support :$supported_locales"
AS_IF([test "x$native" = "xyes"],
[AS_ECHO(["Target architecture : native"]) ],
[AS_ECHO(["Target architecture : generic"]) ])
AS_IF([test "x$WEB_REPO_ROOT" != "x"],
[AS_ECHO(["Git repository for WMaker's Website : $WEB_REPO_ROOT"]) ])
AS_IF([test "x$debug" = "xyes"],
+12
View File
@@ -204,6 +204,11 @@ Note that if you don't have it, @command{configure} will issue a big warning in
this is because JPEG images are often used in themes and for background images
so you probably want this format supported.
@item @emph{libjxl} 0.7.0 or newer
For @emph{JXL} image support,
@uref{https://github.com/libjxl/libjxl}
@item @emph{libgif} 2.2 or @emph{libungif}
For @emph{GIF} image support,
@@ -477,6 +482,9 @@ Disable GIF support in @emph{WRaster} library; when enabled use @file{libgif} or
@item --disable-jpeg
Disable JPEG support in @emph{WRaster} library; when enabled use @file{libjpeg}.
@item --disable-jxl
Disable JPEG-XL support in @emph{WRaster} library; when enabled use @file{libjxl}.
@item --without-libbsd
Refuse use of the @file{libbsd} compatibility library in @emph{WINGs} utility library,
even if your system provides it.
@@ -643,6 +651,10 @@ appropriate requirements and works with this.
Despite all this, if you think there's a use for it and feel in the mood to help, do not hesitate to
discuss on the mailing list @value{emailsupport} to get it working.
@item --enable-native
Enable native CPU optimizations by adding @option{-march=native} to the compiler flags, tuning the
generated code for the build machine at the expense of portability.
@item --with-web-repo=@i{PATH}
Enable generation of HTML documentation to be uploaded to @sc{Window Maker}'s website.
The @file{@i{PATH}} is the directory where you have cloned the homepage's repository.
+8 -8
View File
@@ -12,7 +12,7 @@ necessary.
.SH "OPTIONS"
.TP
.BI \-display " host:display.screen"
specify display to use.
specify the display to use.
On multiheaded machines, Window Maker will automatically manage all screens.
If you want Window Maker to manage only a specific screen, you must supply
the screen number you want to have managed with the
@@ -24,13 +24,13 @@ wmaker -display :0.1
.EE
.TP
.B \-\-dont\-restore
do not restore saved session
do not restore the saved session
.TP
.B \-\-global_defaults_path
print the path where the files for the default configuration are installed and exit
.TP
.B \-\-help
print the list of command line options supported, one per line, and exit
print the list of supported command line options, one per line, and exit
.TP
.BI \-\-locale " locale"
specify the locale (i18n language) to use; Window Maker will also set the variable
@@ -38,7 +38,7 @@ specify the locale (i18n language) to use; Window Maker will also set the variab
which will be inherited by all applications started from Window Maker
.TP
.B \-\-no\-autolaunch
do not launch at start-up the applications that were marked in the configuration as autolanched
do not launch at start-up the applications that were marked in the configuration as autolaunched
.TP
.B \-\-no\-clip
do not show the workspace Clip
@@ -53,7 +53,7 @@ disable the Drawers in the Dock
@!HAVE_INOTIFY@disable the periodic check on the configuration file to reload it automatically
@USE_ICCCM_WMREPLACE@.TP
@USE_ICCCM_WMREPLACE@.B \-\-replace
@USE_ICCCM_WMREPLACE@ask the currently running window manager to let Window Maker take his place
@USE_ICCCM_WMREPLACE@ask the currently running window manager to let Window Maker take its place
.TP
.B \-\-static
do not update or save automatically the configuration
@@ -88,7 +88,7 @@ few options not available using the Attributes Editor.
.B @pkgconfdir@/
All the above-mentioned files are READ from here if not found except
for WMState, which is COPIED from here. No matter where they are read
from, if it's necessary to write configuration changes back into this
from, if it's necessary to write configuration changes back into these
files, user's files will be written to.
.TP
.B ~/GNUstep/Library/WindowMaker/autostart
@@ -108,7 +108,7 @@ might be shut down before the script is executed.
The menu file indicated in WMRootMenu is looked for here...
.TP
.B @pkgdatadir@/
and here, in that order. Unless, the indicated path is an absolute path.
and here, in that order. Unless the indicated path is an absolute path.
.TP
.B ~/GNUstep/Library/WindowMaker/Pixmaps/
Window Maker looks for \fBpixmaps\fP here
@@ -131,7 +131,7 @@ Window Maker looks for theme files here (ibid)
System-wide (Window Maker-specific) pixmaps are located here
.TP
.B @pkgdatadir@/Styles/
System wide styles are here
System-wide styles are here
.TP
.B @pkgdatadir@/Themes/
Guess... ;-)
+26 -17
View File
@@ -3,11 +3,14 @@
wmiv \- quick image viewer using wrlib
.SH SYNOPSIS
.B wmiv
.RI [ \,image(s)\/ | \,directory\/ ]
.RI [ \,image(s)\/ | \,directory\/ | \,archive\/]
.SH OPTIONS
.TP
.B \-\-help
print help text
display this help
.TP
.B \-\-ignore-unknown
ignore unknown image format
.TP
.B \-\-version
print version
@@ -19,21 +22,6 @@ zoom in
[\-]
zoom out
.TP
[Esc]
actual size
.TP
[D]
launch diaporama mode
.TP
[L]
rotate image on the left
.TP
[Q]
quit
.TP
[R]
rotate image on the right
.TP
[â–¸]
next image
.TP
@@ -45,6 +33,27 @@ first image
.TP
[â–ľ]
last image
.TP
[Ctrl+C]
copy image to clipboard
.TP
[D]
start slideshow
.TP
[Esc]
actual size
.TP
[F]
toggle full-screen mode
.TP
[L]
rotate image on the left
.TP
[Q]
quit
.TP
[R]
rotate image on the right
.SH AUTHOR
.B wmiv
is a part of Window Maker. It was written by David Maciejak.
+6 -6
View File
@@ -37,7 +37,7 @@ AC_DEFUN_ONCE([WM_I18N_LANGUAGES],
[AC_ARG_VAR([LINGUAS],
[list of language translations to support (I18N), use 'list' to get the list of supported languages, default: none])dnl
AC_DEFUN([WM_ALL_LANGUAGES],
[m4_esyscmd([( ls wrlib/po/ ; ls WINGs/po/ ; ls po/ ; ls WPrefs.app/po/ ; ls util/po/ ) | sed -n -e '/po$/{s,\.po,,;p}' | sort -u | tr '\n' ' '])])dnl
[m4_esyscmd([( ls wrlib/po/ ; ls WINGs/po/ ; ls po/ ; ls WPrefs.app/po/ ; ls util/po/ ) | sed -n -e '/po$/{s,\.po,,;p;}' | sort -u | tr '\n' ' '])])dnl
dnl We 'divert' the macro to have it executed as soon as the option list have
dnl been processed, so the list of locales will be printed after the configure
dnl options have been parsed, but before any test have been run
@@ -66,11 +66,11 @@ AS_IF([test "x$LINGUAS" != "x"],
supported_locales=""
# This is the list of locales that our archive currently supports
wraster_locales=" m4_esyscmd([ls wrlib/po/ | sed -n '/po$/{s,.po,,;p}' | tr '\n' ' '])"
wings_locales=" m4_esyscmd([ls WINGs/po/ | sed -n '/po$/{s,.po,,;p}' | tr '\n' ' '])"
wmaker_locales=" m4_esyscmd([ls po/ | sed -n '/po$/{s,.po,,;p}' | tr '\n' ' '])"
wprefs_locales=" m4_esyscmd([ls WPrefs.app/po/ | sed -n '/po$/{s,.po,,;p}' | tr '\n' ' '])"
util_locales=" m4_esyscmd([ls util/po/ | sed -n '/po$/{s,.po,,;p}' | tr '\n' ' '])"
wraster_locales=" m4_esyscmd([ls wrlib/po/ | sed -n '/po$/{s,.po,,;p;}' | tr '\n' ' '])"
wings_locales=" m4_esyscmd([ls WINGs/po/ | sed -n '/po$/{s,.po,,;p;}' | tr '\n' ' '])"
wmaker_locales=" m4_esyscmd([ls po/ | sed -n '/po$/{s,.po,,;p;}' | tr '\n' ' '])"
wprefs_locales=" m4_esyscmd([ls WPrefs.app/po/ | sed -n '/po$/{s,.po,,;p;}' | tr '\n' ' '])"
util_locales=" m4_esyscmd([ls util/po/ | sed -n '/po$/{s,.po,,;p;}' | tr '\n' ' '])"
man_locales=" m4_esyscmd([ls doc/ | grep '^[a-z][a-z]\(_[A-Z][A-Z]\)*$' | tr '\n' ' '])"
# If the LINGUAS is specified as a simple '*', then we enable all the languages
+31
View File
@@ -113,6 +113,37 @@ AC_DEFUN_ONCE([WM_IMGFMT_CHECK_JPEG],
]) dnl AC_DEFUN
# WM_IMGFMT_CHECK_JXL
# -------------------
#
# Check for JXL (JPEG XL) file support through 'libjxl'
# The check depends on variable 'enable_jxl' being either:
# yes - detect, fail if not found
# no - do not detect, disable support
# auto - detect, disable if not found
#
# When found, append appropriate stuff in GFXLIBS, and append info to
# the variable 'supported_gfx'
# When not found, append info to variable 'unsupported'
AC_DEFUN_ONCE([WM_IMGFMT_CHECK_JXL],
[WM_LIB_CHECK([JXL], [-ljxl], [JxlDecoderCreate], [$XLFLAGS $XLIBS],
[AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM(
[@%:@include <stdlib.h>
@%:@include <jxl/decode.h>],
[ JxlDecoder* dec = JxlDecoderCreate(NULL);
JxlDecoderDestroy(dec);])],
[],
[AS_ECHO([failed])
AS_ECHO(["$as_me: error: found $CACHEVAR but cannot compile header"])
AS_ECHO(["$as_me: error: - does header 'jxl/decode.h' exists? (is package 'libjxl-dev' missing?)"])
AS_ECHO(["$as_me: error: - version of header is not supported? (report to dev team)"])
AC_MSG_ERROR([JXL library is not usable, cannot continue])])
],
[supported_gfx], [GFXLIBS])dnl
]) dnl AC_DEFUN
# WM_IMGFMT_CHECK_PNG
# -------------------
#
+1 -1
View File
@@ -45,7 +45,7 @@ bs.po Bosnian Ahmet Mulalic <am@lugzdk.ba>
hy.po Armenian Norayr Chilignaryan <chnorik@gmail.com>
nl.po Dutch Alwin <translations@ziggo.nl>
fy.po Frisian Alwin <translations@ziggo.nl>
sr.po Serbian Strahinya Radich <contact@strahinja.org>
sr.po Serbian Strahinya Radich <sr@strahinja.org>
Some translations are based on previous translations by other people.
+2 -2
View File
@@ -3,7 +3,7 @@
# 1998-2006 Dan Pascu
# 2013-2020 Window Maker Developers Team
# This file is distributed under the same license as the Window Maker package.
# Strahinya Radich (Страхиња Радић) <contact@strahinja.org>, 2023.
# Strahinya Radich (Страхиња Радић) <sr@strahinja.org>, 2023.
#
msgid ""
msgstr ""
@@ -11,7 +11,7 @@ msgstr ""
"Report-Msgid-Bugs-To: wmaker-dev@googlegroups.com\n"
"POT-Creation-Date: 2023-11-02 19:22+0100\n"
"PO-Revision-Date: 2023-11-02 19:51+0100\n"
"Last-Translator: Страхиња Радић <contact@strahinja.org>\n"
"Last-Translator: Страхиња Радић <sr@strahinja.org>\n"
"Language-Team: Serbian <sr@li.org>\n"
"Language: sr\n"
"MIME-Version: 1.0\n"
+2 -2
View File
@@ -454,6 +454,7 @@ extern struct WPreferences {
char single_click; /* single click to lauch applications */
int history_lines; /* history of "Run..." dialog */
char cycle_active_head_only; /* Cycle only windows on the active head */
char cycle_all_workspaces; /* Cycle all windows from all workspaces */
char cycle_ignore_minimized; /* Ignore minimized windows when cycling */
char double_click_fullscreen; /* Double click on titlebar maximize a window to full screen*/
char close_rootmenu_left_right_click;/* Close application menu when mouse (left or right) is clicked outside focus */
@@ -478,6 +479,7 @@ extern struct WPreferences {
int hot_corner_delay; /* Delay after which the hot corner is triggered */
int hot_corner_edge; /* Hot corner edge size */
char *hot_corner_actions[4]; /* Action of each corner */
char window_list_app_icons; /* Show app icons in window list */
struct {
#ifdef USE_ICCCM_WMREPLACE
@@ -618,12 +620,10 @@ extern struct wmaker_global_variables {
} shape;
#endif
#ifdef KEEP_XKB_LOCK_STATUS
struct {
Bool supported;
int event_base;
} xkb;
#endif
#ifdef USE_RANDR
struct {
+9 -6
View File
@@ -1186,14 +1186,17 @@ static void create_appicon_from_dock(WWindow *wwin, WApplication *wapp, Window m
wapp->app_icon->running = 1;
wapp->app_icon->icon->owner = mainw;
if (mainw->wm_hints && (mainw->wm_hints->flags & IconWindowHint))
wapp->app_icon->icon->icon_win = mainw->wm_hints->icon_window;
/* Update the icon images */
wIconUpdate(wapp->app_icon->icon);
if (!WFLAGP(wwin, always_user_icon)) {
if (mainw->wm_hints && (mainw->wm_hints->flags & IconWindowHint))
wapp->app_icon->icon->icon_win = mainw->wm_hints->icon_window;
/* Paint it */
wAppIconPaint(wapp->app_icon);
/* Update the icon images */
wIconUpdate(wapp->app_icon->icon);
/* Paint it */
wAppIconPaint(wapp->app_icon);
}
}
}
+23 -8
View File
@@ -245,6 +245,20 @@ void wClientConfigure(WWindow * wwin, XConfigureRequestEvent * xcre)
wwin->flags.maximized &= ~(MAX_VERTICAL | MAX_LEFTHALF | MAX_RIGHTHALF | MAX_MAXIMUS | MAX_CENTRAL);
wWindowConstrainSize(wwin, (unsigned int *)&nwidth, (unsigned int *)&nheight);
/* Ignore tiny position adjustments from client ConfigureRequests to prevent
* window jitters (e.g., VirtualBox sending incremental 1-2px corrections). */
if (wwin->flags.mapped && !wwin->moveresize.active && (xcre->value_mask & (CWX | CWY))) {
int dx = abs(nx - wwin->frame_x);
int dy = abs(ny - wwin->frame_y);
if (dx < 3 && dy < 3) {
/* Keep current position */
nx = wwin->frame_x;
ny = wwin->frame_y;
}
}
wWindowConfigure(wwin, nx, ny, nwidth, nheight);
wwin->old_geometry.x = nx;
wwin->old_geometry.y = ny;
@@ -454,15 +468,16 @@ void wClientCheckProperty(WWindow * wwin, XPropertyEvent * event)
/* update icon */
if ((wwin->wm_hints->flags & IconPixmapHint)
|| (wwin->wm_hints->flags & IconWindowHint)) {
WApplication *wapp;
if (!WFLAGP(wwin, always_user_icon)) {
WApplication *wapp;
if (wwin->flags.miniaturized && wwin->icon)
wIconUpdate(wwin->icon);
if (wwin->flags.miniaturized && wwin->icon)
wIconUpdate(wwin->icon);
wapp = wApplicationOf(wwin->main_window);
if (wapp && wapp->app_icon) {
wIconUpdate(wapp->app_icon->icon);
wAppIconPaint(wapp->app_icon);
wapp = wApplicationOf(wwin->main_window);
if (wapp && wapp->app_icon) {
wIconUpdate(wapp->app_icon->icon);
wAppIconPaint(wapp->app_icon);
}
}
}
+27
View File
@@ -534,6 +534,8 @@ WDefaultEntry optionList[] = {
&wPreferences.hot_corner_edge, getInt, NULL, NULL, NULL},
{"HotCornerActions", "(\"None\", \"None\", \"None\", \"None\")", &wPreferences,
NULL, getPropList, setHotCornerActions, NULL, NULL},
{"WindowListAppIcons", "NO", NULL,
&wPreferences.window_list_app_icons, getBool, NULL, NULL, NULL},
/* style options */
@@ -849,6 +851,8 @@ WDefaultEntry optionList[] = {
&wPreferences.history_lines, getInt, NULL, NULL, NULL},
{"CycleActiveHeadOnly", "NO", NULL,
&wPreferences.cycle_active_head_only, getBool, NULL, NULL, NULL},
{"CycleAllWorkspaces", "NO", NULL,
&wPreferences.cycle_all_workspaces, getBool, NULL, NULL, NULL},
{"CycleIgnoreMinimized", "NO", NULL,
&wPreferences.cycle_ignore_minimized, getBool, NULL, NULL, NULL},
{"DbClickFullScreen", "NO", NULL,
@@ -1301,6 +1305,29 @@ void wReadDefaults(WScreen * scr, WMPropList * new_dict)
}
}
void wReadKeybindings(WScreen *scr, WMPropList *dict)
{
WDefaultEntry *entry;
unsigned int i;
void *tdata;
for (i = 0; i < wlengthof(optionList); i++) {
entry = &optionList[i];
if (entry->convert == getKeybind) {
WMPropList *plvalue = NULL;
if (dict)
plvalue = WMGetFromPLDictionary(dict, entry->plkey);
if (!plvalue)
plvalue = entry->plvalue;
if (plvalue) {
int ok = (*entry->convert)(scr, entry, plvalue, entry->addr, &tdata);
if (ok && entry->update)
(*entry->update)(scr, entry, tdata, entry->extra_data);
}
}
}
}
void wDefaultUpdateIcons(WScreen *scr)
{
WAppIcon *aicon = scr->app_icon_list;
+1
View File
@@ -33,6 +33,7 @@ WDDomain * wDefaultsInitDomain(const char *domain, Bool requireDictionary);
void wDefaultsMergeGlobalMenus(WDDomain *menuDomain);
void wReadDefaults(WScreen *scr, WMPropList *new_dict);
void wReadKeybindings(WScreen *scr, WMPropList *dict);
void wDefaultUpdateIcons(WScreen *scr);
void wReadStaticDefaults(WMPropList *dict);
void wDefaultsCheckDomains(void *arg);
+22 -6
View File
@@ -1083,6 +1083,7 @@ Bool wIconChooserDialog(WScreen *scr, char **file, const char *instance, const c
WMMapWidget(panel->win);
wWindowMap(wwin);
wSetFocusTo(scr, wwin);
while (!panel->done) {
XEvent event;
@@ -1276,9 +1277,9 @@ void wShowInfoPanel(WScreen *scr)
panel->win = WMCreateWindow(scr->wmscreen, "info");
WMGetScaleBaseFromSystemFont(scr->wmscreen, &wmScaleWidth, &wmScaleHeight);
#if defined(HAVE_MALLOC_H) && defined(HAVE_MALLINFO2) && defined(DEBUG)
pwidth = WMScaleX(412);
pwidth = WMScaleX(432);
#else
pwidth = WMScaleX(382);
pwidth = WMScaleX(392);
#endif
pheight = WMScaleY(270);
WMResizeWidget(panel->win, pwidth, pheight);
@@ -1344,7 +1345,7 @@ void wShowInfoPanel(WScreen *scr)
panel->copyrL = WMCreateLabel(panel->win);
WMResizeWidget(panel->copyrL, WMScaleX(360), WMScaleY(60));
WMMoveWidget(panel->copyrL, WMScaleX(15), WMScaleY(210));
WMMoveWidget(panel->copyrL, WMScaleX(15), WMScaleY(215));
WMSetLabelTextAlignment(panel->copyrL, WALeft);
snprintf(buffer, sizeof(buffer), COPYRIGHT_TEXT, current_year);
@@ -1455,7 +1456,7 @@ void wShowInfoPanel(WScreen *scr)
strbuf = wstrappend(strbuf, _("Xinerama: "));
{
char tmp[128];
snprintf(tmp, sizeof(tmp) - 1, _("%d head(s) found"), scr->xine_info.count);
snprintf(tmp, sizeof(tmp) - 1, scr->xine_info.count > 1 ? _("%d head(s) found") : _("%d head found"), scr->xine_info.count);
strbuf = wstrappend(strbuf, tmp);
}
#endif
@@ -1471,9 +1472,9 @@ void wShowInfoPanel(WScreen *scr)
panel->infoL = WMCreateLabel(panel->win);
#if defined(HAVE_MALLOC_H) && defined(HAVE_MALLINFO2) && defined(DEBUG)
WMResizeWidget(panel->infoL, WMScaleX(380), WMScaleY(100));
WMResizeWidget(panel->infoL, WMScaleX(430), WMScaleY(105));
#else
WMResizeWidget(panel->infoL, WMScaleX(350), WMScaleY(100));
WMResizeWidget(panel->infoL, WMScaleX(370), WMScaleY(105));
#endif
WMMoveWidget(panel->infoL, WMScaleX(15), WMScaleY(115));
WMSetLabelText(panel->infoL, strbuf);
@@ -1641,6 +1642,8 @@ typedef struct _CrashPanel {
int action; /* what to do after */
KeyCode retKey;
KeyCode upKey;
KeyCode downKey;
} CrashPanel;
@@ -1650,6 +1653,17 @@ static void handleKeyPress(XEvent * event, void *clientData)
if (event->xkey.keycode == panel->retKey) {
WMPerformButtonClick(panel->okB);
return;
}
if (event->xkey.keycode == panel->upKey) {
WMSelectPopUpButtonPreviousItem(panel->whatP);
return;
}
if (event->xkey.keycode == panel->downKey) {
WMSelectPopUpButtonNextItem(panel->whatP);
return;
}
}
@@ -1721,6 +1735,8 @@ int wShowCrashingDialogPanel(int whatSig)
panel = wmalloc(sizeof(CrashPanel));
panel->retKey = XKeysymToKeycode(dpy, XK_Return);
panel->upKey = XKeysymToKeycode(dpy, XK_Up);
panel->downKey = XKeysymToKeycode(dpy, XK_Down);
panel->win = WMCreateWindow(scr, "crashingDialog");
WMResizeWidget(panel->win, PWIDTH, PHEIGHT);
+41 -10
View File
@@ -49,9 +49,7 @@
#include <X11/extensions/Xrandr.h>
#endif
#ifdef KEEP_XKB_LOCK_STATUS
#include <X11/XKBlib.h>
#endif /* KEEP_XKB_LOCK_STATUS */
#include "WindowMaker.h"
#include "window.h"
@@ -103,7 +101,9 @@ static void handleKeyPress(XEvent *event);
static void handleFocusIn(XEvent *event);
static void handleMotionNotify(XEvent *event);
static void handleVisibilityNotify(XEvent *event);
#ifdef HAVE_INOTIFY
static void handle_inotify_events(void);
#endif
static void handle_selection_request(XSelectionRequestEvent *event);
static void handle_selection_clear(XSelectionClearEvent *event);
static void wdelete_death_handler(WMagicNumber id);
@@ -569,11 +569,26 @@ static void handleExtensions(XEvent * event)
handleShapeNotify(event);
}
#endif
if (w_global.xext.xkb.supported && event->type == w_global.xext.xkb.event_base) {
XkbEvent *xkbevent = (XkbEvent *) event;
if (xkbevent->any.xkb_type == XkbNewKeyboardNotify) {
int j;
WScreen *scr;
for (j = 0; j < w_global.screen_count; j++) {
scr = wScreenWithNumber(j);
wReadKeybindings(scr, w_global.domain.wmaker->dictionary);
}
}
#ifdef KEEP_XKB_LOCK_STATUS
if (wPreferences.modelock && (event->type == w_global.xext.xkb.event_base)) {
handleXkbIndicatorStateNotify((XkbEvent *) event);
else {
if (wPreferences.modelock && (xkbevent->any.xkb_type == XkbIndicatorStateNotify)) {
handleXkbIndicatorStateNotify((XkbEvent *) event);
}
}
#endif /*KEEP_XKB_LOCK_STATUS */
}
#endif /*KEEP_XKB_LOCK_STATUS */
#ifdef USE_RANDR
if (w_global.xext.randr.supported && event->type == (w_global.xext.randr.event_base + RRScreenChangeNotify)) {
/* From xrandr man page: "Clients must call back into Xlib using
@@ -1275,6 +1290,7 @@ static void handleXkbIndicatorStateNotify(XkbEvent *event)
WScreen *scr;
XkbStateRec staterec;
int i;
(void) event;
for (i = 0; i < w_global.screen_count; i++) {
scr = wScreenWithNumber(i);
@@ -1942,16 +1958,31 @@ static void handleKeyPress(XEvent * event)
#define CORNER_BOTTOMLEFT 3
#define CORNER_BOTTOMRIGHT 4
static int get_corner(WMRect rect, WMPoint p)
static inline int get_corner(WMRect rect, WMPoint p)
{
if (p.x <= (rect.pos.x + wPreferences.hot_corner_edge) && p.y <= (rect.pos.y + wPreferences.hot_corner_edge))
const int edge = wPreferences.hot_corner_edge;
const int left_edge = rect.pos.x + edge;
const int right_edge = rect.pos.x + rect.size.width - edge;
const int top_edge = rect.pos.y + edge;
const int bottom_edge = rect.pos.y + rect.size.height - edge;
int in_left = (p.x <= left_edge);
int in_right = (p.x >= right_edge);
int in_top = (p.y <= top_edge);
int in_bottom = (p.y >= bottom_edge);
if (!(in_left || in_right) || !(in_top || in_bottom))
return CORNER_NONE;
if (in_left && in_top)
return CORNER_TOPLEFT;
if (p.x >= (rect.pos.x + rect.size.width - wPreferences.hot_corner_edge) && p.y <= (rect.pos.y + wPreferences.hot_corner_edge))
if (in_right && in_top)
return CORNER_TOPRIGHT;
if (p.x <= (rect.pos.x + wPreferences.hot_corner_edge) && p.y >= (rect.pos.y + rect.size.height - wPreferences.hot_corner_edge))
if (in_left && in_bottom)
return CORNER_BOTTOMLEFT;
if (p.x >= (rect.pos.x + rect.size.width - wPreferences.hot_corner_edge) && p.y >= (rect.pos.y + rect.size.height - wPreferences.hot_corner_edge))
if (in_right && in_bottom)
return CORNER_BOTTOMRIGHT;
return CORNER_NONE;
}
+7 -7
View File
@@ -58,14 +58,14 @@ static void paintButton(WCoreWindow * button, WTexture * texture,
static void updateTitlebar(WFrameWindow * fwin);
static void allocFrameBorderPixel(Colormap colormap, const char *color_name, unsigned long **pixel);
static void allocFrameBorderPixel(WFrameWindow *fwin, const char *color_name, unsigned long **pixel);
static void allocFrameBorderPixel(Colormap colormap, const char *color_name, unsigned long **pixel) {
static void allocFrameBorderPixel(WFrameWindow *fwin, const char *color_name, unsigned long **pixel) {
XColor xcol;
*pixel = NULL;
if (! wGetColorForColormap(colormap, color_name, &xcol))
if (! wGetColorForColormap(fwin->screen_ptr, fwin->colormap, color_name, &xcol))
return;
*pixel = wmalloc(sizeof(unsigned long));
@@ -412,9 +412,9 @@ void wFrameWindowUpdateBorders(WFrameWindow * fwin, int flags)
checkTitleSize(fwin);
allocFrameBorderPixel(fwin->colormap, WMGetColorRGBDescription(scr->frame_border_color), &fwin->border_pixel);
allocFrameBorderPixel(fwin->colormap, WMGetColorRGBDescription(scr->frame_focused_border_color), &fwin->focused_border_pixel);
allocFrameBorderPixel(fwin->colormap, WMGetColorRGBDescription(scr->frame_selected_border_color), &fwin->selected_border_pixel);
allocFrameBorderPixel(fwin, WMGetColorRGBDescription(scr->frame_border_color), &fwin->border_pixel);
allocFrameBorderPixel(fwin, WMGetColorRGBDescription(scr->frame_focused_border_color), &fwin->focused_border_pixel);
allocFrameBorderPixel(fwin, WMGetColorRGBDescription(scr->frame_selected_border_color), &fwin->selected_border_pixel);
if (flags & WFF_SELECTED) {
if (fwin->selected_border_pixel)
@@ -1394,7 +1394,7 @@ static void handleButtonExpose(WObjDescriptor * desc, XEvent * event)
#ifdef XKB_BUTTON_HINT
if (button == fwin->language_button) {
if (wPreferences.modelock)
if (!fwin->flags.hide_language_button)
paintButton(button, fwin->title_texture[fwin->flags.state],
WMColorPixel(fwin->title_color[fwin->flags.state]),
fwin->languagebutton_image, False);
+8
View File
@@ -331,6 +331,14 @@ void wIconChangeTitle(WIcon *icon, WWindow *wwin)
/* Set the new one, using two methods to identify
the icon name or switch back to window name */
icon->icon_name = wNETWMGetIconName(wwin->client_win);
if (icon->icon_name) {
/* treat empty string as unset */
if (icon->icon_name[0] == '\0') {
XFree(icon->icon_name);
icon->icon_name = NULL;
}
}
if (!icon->icon_name)
if (!wGetIconName(dpy, wwin->client_win, &icon->icon_name))
icon->icon_name = wNETWMGetWindowName(wwin->client_win);
+6 -4
View File
@@ -326,7 +326,8 @@ void ExecuteShellCommand(WScreen *scr, const char *command)
#endif
execl(shell, shell, "-c", command, NULL);
werror("could not execute %s -c %s", shell, command);
Exit(-1);
/* exec failed in child -- exit immediately without running parent cleanup */
_exit(127);
} else if (pid < 0) {
werror("cannot fork a new process");
} else {
@@ -377,10 +378,10 @@ Bool RelaunchWindow(WWindow *wwin)
setsid();
#endif
/* argv is not null-terminated */
char **a = (char **) malloc(argc + 1);
char **a = malloc((argc + 1) * sizeof(char *));
if (! a) {
werror("out of memory trying to relaunch the application");
Exit(-1);
_exit(127);
}
int i;
@@ -389,7 +390,8 @@ Bool RelaunchWindow(WWindow *wwin)
a[i] = NULL;
execvp(a[0], a);
Exit(-1);
/* exec failed in child -- exit immediately */
_exit(127);
} else if (pid < 0) {
werror("cannot fork a new process");
+31
View File
@@ -267,6 +267,7 @@ WMenuEntry *wMenuInsertCallback(WMenu *menu, int index, const char *text,
entry = wmalloc(sizeof(WMenuEntry));
entry->flags.enabled = 1;
entry->text = wstrdup(text);
entry->icon = NULL;
entry->cascade = -1;
entry->clientdata = clientdata;
entry->callback = callback;
@@ -369,6 +370,9 @@ void wMenuRemoveItem(WMenu * menu, int index)
if (menu->entries[index]->text)
wfree(menu->entries[index]->text);
if (menu->entries[index]->icon)
wPixmapDestroy(menu->entries[index]->icon);
if (menu->entries[index]->rtext)
wfree(menu->entries[index]->rtext);
@@ -499,6 +503,10 @@ void wMenuRealize(WMenu * menu)
text = menu->entries[i]->text;
width = WMWidthOfString(scr->menu_entry_font, text, strlen(text)) + 10;
if (wPreferences.window_list_app_icons && menu->entries[i]->icon) {
width += menu->entries[i]->icon->width + 4;
}
if (menu->entries[i]->flags.indicator) {
width += MENU_INDICATOR_SPACE;
}
@@ -562,6 +570,9 @@ void wMenuDestroy(WMenu * menu, int recurse)
wfree(menu->entries[i]->text);
if (menu->entries[i]->icon)
wPixmapDestroy(menu->entries[i]->icon);
if (menu->entries[i]->rtext)
wfree(menu->entries[i]->rtext);
#ifdef USER_MENU
@@ -711,6 +722,26 @@ static void paintEntry(WMenu * menu, int index, int selected)
if (entry->flags.indicator)
x += MENU_INDICATOR_SPACE + 2;
if (wPreferences.window_list_app_icons && entry->icon && entry->icon->image != None) {
int ix = x;
int iy = y + (h - entry->icon->height) / 2;
if (entry->icon->mask != None) {
XSetClipMask(dpy, scr->copy_gc, entry->icon->mask);
XSetClipOrigin(dpy, scr->copy_gc, ix, iy);
} else {
XSetClipMask(dpy, scr->copy_gc, None);
XSetClipOrigin(dpy, scr->copy_gc, 0, 0);
}
XCopyArea(dpy, entry->icon->image, win, scr->copy_gc,
0, 0, entry->icon->width, entry->icon->height, ix, iy);
XSetClipMask(dpy, scr->copy_gc, None);
XSetClipOrigin(dpy, scr->copy_gc, 0, 0);
x += entry->icon->width + 4;
}
WMDrawString(scr->wmscreen, win, color, scr->menu_entry_font,
x, 3 + y + wPreferences.menu_text_clearance, entry->text, strlen(entry->text));
+1
View File
@@ -46,6 +46,7 @@ typedef struct WMenuEntry {
int order;
char *text; /* entry text */
char *rtext; /* text to show in the right part */
struct WPixmap *icon; /* optional icon displayed before the text */
void (*callback)(struct WMenu *menu, struct WMenuEntry *entry);
void (*free_cdata)(void *data); /* proc to be used to free clientdata */
void *clientdata; /* data to pass to callback */
+71 -8
View File
@@ -124,6 +124,44 @@ Bool wGetIconName(Display *dpy, Window win, char **iconname)
return False;
}
int WMStrEqual(const char *x, const char *y)
{
if ((x == NULL) && (y == NULL))
return 1;
if ((x == NULL) || (y == NULL))
return 0;
return (strcmp(x, y) == 0);
}
int WMPLGetBool(WMPropList *value)
{
char *val;
if (!WMIsPLString(value))
return 0;
val = WMGetFromPLString(value);
if (val == NULL)
return 0;
if ((val[1] == '\0' &&
(val[0] == 'y' || val[0] == 'Y' || val[0] == 'T' ||
val[0] == 't' || val[0] == '1')) ||
(strcasecmp(val, "YES") == 0 || strcasecmp(val, "TRUE") == 0)) {
return 1;
} else if ((val[1] == '\0' &&
(val[0] == 'n' || val[0] == 'N' || val[0] == 'F' ||
val[0] == 'f' || val[0] == '0')) ||
(strcasecmp(val, "NO") == 0 || strcasecmp(val, "FALSE") == 0)) {
return 0;
} else {
wwarning(_("can't convert \"%s\" to boolean"), val);
return 0;
}
}
static void eatExpose(void)
{
XEvent event, foo;
@@ -259,13 +297,22 @@ void slide_windows(Window wins[], int n, int from_x, int from_y, int to_x, int t
eatExpose();
}
/* find the start of a UTF-8 character at or before the given position */
static int utf8_find_char_start(const char *string, int pos)
{
while (pos > 0 && (string[pos] & 0xC0) == 0x80) {
pos--;
}
return pos;
}
char *ShrinkString(WMFont *font, const char *string, int width)
{
int w, w1 = 0;
int p;
char *pos;
char *text;
int p1, p2, t;
int p1, p2, t, utf8_safe_pos;
p = strlen(string);
w = WMWidthOfString(font, string, p);
@@ -302,18 +349,34 @@ char *ShrinkString(WMFont *font, const char *string, int width)
p1 = 0;
p2 = p;
t = (p2 - p1) / 2;
while (p2 > p1 && p1 != t) {
w = WMWidthOfString(font, &string[p - t], t);
while (p2 > p1) {
int old_t = t;
/* ensure we cut at UTF-8 character boundary */
utf8_safe_pos = utf8_find_char_start(string, p - t);
t = p - utf8_safe_pos;
w = WMWidthOfString(font, &string[utf8_safe_pos], t);
if (w > width) {
p2 = t;
t = p1 + (p2 - p1) / 2;
} else if (w < width) {
p1 = t;
t = p1 + (p2 - p1) / 2;
} else
p2 = p1 = t;
} else {
p1 = t;
break;
}
/* Calculate new t for next iteration */
t = p1 + (p2 - p1) / 2;
/* Prevent infinite loop if we're not making progress */
if (t == old_t || (p2 - p1) <= 1) {
break;
}
}
strcat(text, &string[p - p1]);
/* ensure final cut is at UTF-8 character boundary */
utf8_safe_pos = utf8_find_char_start(string, p - p1);
strcat(text, &string[utf8_safe_pos]);
return text;
}
+2
View File
@@ -54,4 +54,6 @@ char *GetShortcutKey(WShortKey key);
char *EscapeWM_CLASS(const char *name, const char *class);
char *StrConcatDot(const char *a, const char *b);
char *GetCommandForWindow(Window win);
int WMStrEqual(const char *x, const char *y);
int WMPLGetBool(WMPropList *value);
#endif
+42 -4
View File
@@ -19,6 +19,7 @@
*/
#include "wconfig.h"
#include "wmspec.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
@@ -1916,7 +1917,7 @@ int wMouseMoveWindow(WWindow * wwin, XEvent * ev)
break;
case ButtonRelease:
if (event.xbutton.button != ev->xbutton.button)
if (!wwin->moveresize.active && (event.xbutton.button != ev->xbutton.button))
break;
if (started) {
@@ -1964,6 +1965,10 @@ int wMouseMoveWindow(WWindow * wwin, XEvent * ev)
WMUnmapWidget(scr->gview);
}
}
if (wwin->moveresize.active) {
XUngrabPointer(dpy, CurrentTime);
wwin->moveresize.active = 0;
}
done = True;
break;
@@ -2101,8 +2106,13 @@ void wMouseResizeWindow(WWindow * wwin, XEvent * ev)
wwarning("internal error: tryein");
return;
}
orig_x = ev->xbutton.x_root;
orig_y = ev->xbutton.y_root;
if (ev->type == MotionNotify) {
orig_x = ev->xmotion.x_root;
orig_y = ev->xmotion.y_root;
} else {
orig_x = ev->xbutton.x_root;
orig_y = ev->xbutton.y_root;
}
started = 0;
wUnselectWindows(scr);
@@ -2113,6 +2123,29 @@ void wMouseResizeWindow(WWindow * wwin, XEvent * ev)
shiftl = XKeysymToKeycode(dpy, XK_Shift_L);
shiftr = XKeysymToKeycode(dpy, XK_Shift_R);
if (wwin->moveresize.active) {
int direction = wwin->moveresize.resize_edge;
res = 0;
is_resizebar = 0;
if (direction == _NET_WM_MOVERESIZE_SIZE_TOP)
res |= UP;
else if (direction == _NET_WM_MOVERESIZE_SIZE_BOTTOM)
res |= DOWN;
else if (direction == _NET_WM_MOVERESIZE_SIZE_LEFT)
res |= LEFT;
else if (direction == _NET_WM_MOVERESIZE_SIZE_RIGHT)
res |= RIGHT;
else if (direction == _NET_WM_MOVERESIZE_SIZE_TOPLEFT)
res |= (UP | LEFT);
else if (direction == _NET_WM_MOVERESIZE_SIZE_TOPRIGHT)
res |= (UP | RIGHT);
else if (direction == _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT)
res |= (DOWN | LEFT);
else if (direction == _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT)
res |= (DOWN | RIGHT);
}
while (1) {
WMMaskEvent(dpy, KeyPressMask | ButtonMotionMask
| ButtonReleaseMask | PointerMotionHintMask | ButtonPressMask | ExposureMask, &event);
@@ -2269,7 +2302,7 @@ void wMouseResizeWindow(WWindow * wwin, XEvent * ev)
break;
case ButtonRelease:
if (event.xbutton.button != ev->xbutton.button)
if (!wwin->moveresize.active && (event.xbutton.button != ev->xbutton.button))
break;
if (started) {
@@ -2291,6 +2324,11 @@ void wMouseResizeWindow(WWindow * wwin, XEvent * ev)
wWindowConfigure(wwin, fx, fy, fw, fh - vert_border);
wWindowSynthConfigureNotify(wwin);
}
if (wwin->moveresize.active) {
XUngrabPointer(dpy, CurrentTime);
wwin->moveresize.active = 0;
}
return;
default:
+2 -2
View File
@@ -197,8 +197,8 @@ void PropSetIconTileHint(WScreen * scr, RImage * image)
if (image->format == RRGBAFormat) {
memcpy(&tmp[4], image->data, image->width * image->height * 4);
} else {
char *ptr = (char *)(tmp + 4);
char *src = (char *)image->data;
unsigned char *ptr = tmp + 4;
unsigned char *src = (unsigned char *)image->data;
for (y = 0; y < image->height; y++) {
for (x = 0; x < image->width; x++) {
+13 -2
View File
@@ -35,8 +35,16 @@
#include "resources.h"
#include "screen.h"
int wGetColorForColormap(Colormap colormap, const char *color_name, XColor *color)
int wGetColorForColormap(WScreen *scr, Colormap colormap, const char *color_name, XColor *color)
{
if (scr->w_visual->class == TrueColor) {
XColor dummy_exact;
if (!XLookupColor(dpy, colormap, color_name, &dummy_exact, color)) {
wwarning(_("could not lookup color \"%s\""), color_name);
return False;
}
return True;
}
if (!XParseColor(dpy, colormap, color_name, color)) {
wwarning(_("could not parse color \"%s\""), color_name);
return False;
@@ -50,11 +58,14 @@ int wGetColorForColormap(Colormap colormap, const char *color_name, XColor *colo
int wGetColor(WScreen *scr, const char *color_name, XColor *color)
{
return wGetColorForColormap(scr->w_colormap, color_name, color);
return wGetColorForColormap(scr, scr->w_colormap, color_name, color);
}
void wFreeColor(WScreen * scr, unsigned long pixel)
{
if (scr->w_visual->class == TrueColor)
return;
if (pixel != scr->white_pixel && pixel != scr->black_pixel) {
unsigned long colors[1];
+1 -1
View File
@@ -21,7 +21,7 @@
#ifndef WMRESOURCES_H_
#define WMRESOURCES_H_
int wGetColorForColormap(Colormap colormap, const char *color_name, XColor *color);
int wGetColorForColormap(WScreen *scr, Colormap colormap, const char *color_name, XColor *color);
int wGetColor(WScreen *scr, const char *color_name, XColor *color);
void wFreeColor(WScreen *scr, unsigned long pixel);
+5 -9
View File
@@ -32,9 +32,7 @@
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#ifdef KEEP_XKB_LOCK_STATUS
#include <X11/XKBlib.h>
#endif /* KEEP_XKB_LOCK_STATUS */
#ifdef USE_RANDR
#include <X11/extensions/Xrandr.h>
#endif
@@ -663,9 +661,11 @@ WScreen *wScreenInit(int screen_number)
/* Only GroupLock doesn't work correctly in my system since right-alt
* can change mode while holding it too - ]d
*/
if (w_global.xext.xkb.supported) {
XkbSelectEvents(dpy, XkbUseCoreKbd, XkbStateNotifyMask, XkbStateNotifyMask);
}
if (w_global.xext.xkb.supported)
XkbSelectEvents(dpy, XkbUseCoreKbd, XkbIndicatorStateNotifyMask|XkbNewKeyboardNotifyMask, XkbIndicatorStateNotifyMask|XkbNewKeyboardNotifyMask);
#else
if (w_global.xext.xkb.supported)
XkbSelectEvents(dpy, XkbUseCoreKbd, XkbNewKeyboardNotifyMask, XkbNewKeyboardNotifyMask);
#endif /* KEEP_XKB_LOCK_STATUS */
#ifdef USE_RANDR
@@ -1348,11 +1348,7 @@ void ScreenCapture(WScreen *scr, int mode)
}
if (img) {
#ifdef USE_PNG
if (RSaveTitledImage(img, filepath, (char *)(filetype + 1), "Screenshot from Window Maker")) {
#else
if (RSaveTitledImage(img, filepath, (char *)(filetype + 1), "Screenshot from Window Maker")) {
#endif
scale_img = RSmoothScaleImage(img, scr->scr_width / 10, scr->scr_height / 10);
showMiniScreenshot(scr, scale_img);
RReleaseImage(scale_img);
+6 -49
View File
@@ -123,35 +123,6 @@ static void make_keys(void)
sNo = WMCreatePLString("No");
}
static int getBool(WMPropList * value)
{
char *val;
if (!WMIsPLString(value)) {
return 0;
}
val = WMGetFromPLString(value);
if (val == NULL)
return 0;
if ((val[1] == '\0' && (val[0] == 'y' || val[0] == 'Y'))
|| strcasecmp(val, "YES") == 0) {
return 1;
} else if ((val[1] == '\0' && (val[0] == 'n' || val[0] == 'N'))
|| strcasecmp(val, "NO") == 0) {
return 0;
} else {
int i;
if (sscanf(val, "%i", &i) == 1) {
return (i != 0);
} else {
wwarning(_("can't convert \"%s\" to boolean"), val);
return 0;
}
}
}
static unsigned getInt(WMPropList * value)
{
char *val;
@@ -423,11 +394,11 @@ static WSavedState *getWindowState(WScreen * scr, WMPropList * win_state)
value = WMGetFromPLDictionary(win_state, sShaded);
if (value != NULL)
state->shaded = getBool(value);
state->shaded = WMPLGetBool(value);
value = WMGetFromPLDictionary(win_state, sMiniaturized);
if (value != NULL)
state->miniaturized = getBool(value);
state->miniaturized = WMPLGetBool(value);
value = WMGetFromPLDictionary(win_state, sMaximized);
if (value != NULL) {
@@ -436,7 +407,7 @@ static WSavedState *getWindowState(WScreen * scr, WMPropList * win_state)
value = WMGetFromPLDictionary(win_state, sHidden);
if (value != NULL)
state->hidden = getBool(value);
state->hidden = WMPLGetBool(value);
value = WMGetFromPLDictionary(win_state, sShortcutMask);
if (value != NULL) {
@@ -456,20 +427,6 @@ static WSavedState *getWindowState(WScreen * scr, WMPropList * win_state)
return state;
}
static inline int is_same(const char *x, const char *y)
{
if ((x == NULL) && (y == NULL))
return 1;
if ((x == NULL) || (y == NULL))
return 0;
if (strcmp(x, y) == 0)
return 1;
else
return 0;
}
void wSessionRestoreState(WScreen *scr)
{
WSavedState *state;
@@ -556,9 +513,9 @@ void wSessionRestoreState(WScreen *scr)
if (dock != NULL) {
for (j = 0; j < dock->max_icons; j++) {
btn = dock->icon_array[j];
if (btn && is_same(instance, btn->wm_instance) &&
is_same(class, btn->wm_class) &&
is_same(command, btn->command) &&
if (btn && WMStrEqual(instance, btn->wm_instance) &&
WMStrEqual(class, btn->wm_class) &&
WMStrEqual(command, btn->command) &&
!btn->launching) {
found = 1;
break;
+4 -3
View File
@@ -41,9 +41,7 @@
#ifdef USE_XSHAPE
#include <X11/extensions/shape.h>
#endif
#ifdef KEEP_XKB_LOCK_STATUS
#include <X11/XKBlib.h>
#endif
#ifdef USE_RANDR
#include <X11/extensions/Xrandr.h>
#endif
@@ -601,12 +599,15 @@ void StartUp(Bool defaultScreenOnly)
w_global.xext.randr.supported = XRRQueryExtension(dpy, &w_global.xext.randr.event_base, &j);
#endif
#ifdef KEEP_XKB_LOCK_STATUS
w_global.xext.xkb.supported = XkbQueryExtension(dpy, NULL, &w_global.xext.xkb.event_base, NULL, NULL, NULL);
#ifdef KEEP_XKB_LOCK_STATUS
if (wPreferences.modelock && !w_global.xext.xkb.supported) {
wwarning(_("XKB is not supported. KbdModeLock is automatically disabled."));
wPreferences.modelock = 0;
}
#else
if (!w_global.xext.xkb.supported)
wwarning(_("XKB is not supported."));
#endif
if (defaultScreenOnly)
+50
View File
@@ -48,6 +48,50 @@ static int initialized = 0;
static void observer(void *self, WMNotification * notif);
static void wsobserver(void *self, WMNotification * notif);
static WPixmap *switchMenuIconForWindow(WScreen *scr, WWindow *wwin)
{
RImage *image = NULL;
WPixmap *pix;
WApplication *wapp;
int max_size;
if (!scr || !wwin)
return NULL;
max_size = WMFontHeight(scr->menu_entry_font) + 2;
if (max_size < 12)
max_size = 12;
/* Prefer the actual appicon image when available */
wapp = wApplicationOf(wwin->main_window);
if (wapp && wapp->app_icon && wapp->app_icon->icon && wapp->app_icon->icon->file_image) {
image = RRetainImage(wapp->app_icon->icon->file_image);
}
/* Fall back to _NET_WM_ICON, then the default icon */
if (!image && !WFLAGP(wwin, always_user_icon) && wwin->net_icon_image)
image = RRetainImage(wwin->net_icon_image);
if (!image)
image = get_icon_image(scr, wwin->wm_instance, wwin->wm_class, max_size);
if (!image)
return NULL;
image = wIconValidateIconSize(image, max_size);
if (!image)
return NULL;
pix = wmalloc(sizeof(WPixmap));
memset(pix, 0, sizeof(WPixmap));
RConvertImageMask(scr->rcontext, image, &pix->image, &pix->mask, 128);
pix->width = image->width;
pix->height = image->height;
pix->depth = scr->w_depth;
RReleaseImage(image);
return pix;
}
/*
* FocusWindow
*
@@ -216,6 +260,8 @@ void UpdateSwitchMenu(WScreen * scr, WWindow * wwin, int action)
entry = wMenuInsertCallback(switchmenu, idx, t, focusWindow, wwin);
wfree(t);
entry->icon = switchMenuIconForWindow(scr, wwin);
entry->flags.indicator = 1;
entry->rtext = wmalloc(MAX_WORKSPACENAME_WIDTH + 8);
if (IS_OMNIPRESENT(wwin))
@@ -273,6 +319,7 @@ void UpdateSwitchMenu(WScreen * scr, WWindow * wwin, int action)
if (entry->rtext) {
int idx = -1;
char *t, *rt;
WPixmap *ipix;
int it, ion;
if (IS_OMNIPRESENT(wwin)) {
@@ -285,6 +332,8 @@ void UpdateSwitchMenu(WScreen * scr, WWindow * wwin, int action)
rt = entry->rtext;
entry->rtext = NULL;
ipix = entry->icon;
entry->icon = NULL;
t = entry->text;
entry->text = NULL;
@@ -300,6 +349,7 @@ void UpdateSwitchMenu(WScreen * scr, WWindow * wwin, int action)
entry = wMenuInsertCallback(switchmenu, idx, t, focusWindow, wwin);
wfree(t);
entry->rtext = rt;
entry->icon = ipix;
entry->flags.indicator = 1;
entry->flags.indicator_type = it;
entry->flags.indicator_on = ion;
+9 -3
View File
@@ -79,7 +79,7 @@ static short int label_height;
static int canReceiveFocus(WWindow *wwin)
{
if (wwin->frame->workspace != wwin->screen_ptr->current_workspace)
if (!wPreferences.cycle_all_workspaces && wwin->frame->workspace != wwin->screen_ptr->current_workspace)
return 0;
if (wPreferences.cycle_active_head_only &&
@@ -90,7 +90,7 @@ static int canReceiveFocus(WWindow *wwin)
return 0;
if (!wwin->flags.mapped) {
if (!wwin->flags.shaded && !wwin->flags.miniaturized && !wwin->flags.hidden)
if (!wwin->flags.shaded && !wwin->flags.miniaturized && !wwin->flags.hidden && !wPreferences.cycle_all_workspaces)
return 0;
else
return -1;
@@ -186,12 +186,18 @@ static void addIconForWindow(WSwitchPanel *panel, WMWidget *parent, WWindow *wwi
{
WMFrame *icon = WMCreateFrame(parent);
RImage *image = NULL;
WApplication *wapp;
WMSetFrameRelief(icon, WRFlat);
WMResizeWidget(icon, icon_tile_size, icon_tile_size);
WMMoveWidget(icon, x, y);
if (!WFLAGP(wwin, always_user_icon) && wwin->net_icon_image)
wapp = wApplicationOf(wwin->main_window);
if (wapp && wapp->app_icon && wapp->app_icon->icon && wapp->app_icon->icon->file_image) {
image = RRetainImage(wapp->app_icon->icon->file_image);
}
if (!image && !WFLAGP(wwin, always_user_icon) && wwin->net_icon_image)
image = RRetainImage(wwin->net_icon_image);
/* get_icon_image() includes the default icon image */
+3 -36
View File
@@ -43,14 +43,13 @@
#include "icon.h"
#include "misc.h"
#define APPLY_VAL(value, flag, attrib) \
if (value) {attr->flag = getBool(attrib, value); \
if (mask) mask->flag = 1;}
#define APPLY_VAL(value, flag, attrib) \
if (value) {attr->flag = WMPLGetBool(value); \
if (mask) mask->flag = 1;}
/* Local stuff */
/* type converters */
static int getBool(WMPropList *, WMPropList *);
static char *getString(WMPropList *, WMPropList *);
static WMPropList *ANoTitlebar = NULL;
static WMPropList *ANoResizebar;
@@ -634,38 +633,6 @@ void wDefaultPurgeInfo(const char *instance, const char *class)
WMPLSetCaseSensitive(False);
}
/* --------------------------- Local ----------------------- */
static int getBool(WMPropList * key, WMPropList * value)
{
char *val;
if (!WMIsPLString(value)) {
wwarning(_("Wrong option format for key \"%s\". Should be %s."),
WMGetFromPLString(key), "Boolean");
return 0;
}
val = WMGetFromPLString(value);
if ((val[1] == '\0' && (val[0] == 'y' || val[0] == 'Y' || val[0] == 'T' || val[0] == 't' || val[0] == '1'))
|| (strcasecmp(val, "YES") == 0 || strcasecmp(val, "TRUE") == 0)) {
return 1;
} else if ((val[1] == '\0'
&& (val[0] == 'n' || val[0] == 'N' || val[0] == 'F' || val[0] == 'f' || val[0] == '0'))
|| (strcasecmp(val, "NO") == 0 || strcasecmp(val, "FALSE") == 0)) {
return 0;
} else {
wwarning(_("can't convert \"%s\" to boolean"), val);
/* We return False if we can't convert to BOOLEAN.
* This is because all options defaults to False.
* -1 is not checked and thus is interpreted as True,
* which is not good.*/
return 0;
}
}
/* WARNING: Do not free the value returned by this function!! */
static char *getString(WMPropList * key, WMPropList * value)
{
+15 -19
View File
@@ -681,6 +681,15 @@ WWindow *wManageWindow(WScreen *scr, Window window)
return NULL;
}
/* Some applications create placeholder windows with 1x1 size
* (e.g. VirtualBox internal windows). Don't manage those initial
* 1x1 windows — wait for a proper ConfigureNotify/MapRequest with
* a real size. */
if (wattribs.width <= 1 && wattribs.height <= 1) {
XUngrabServer(dpy);
return NULL;
}
wm_state = PropGetWindowState(window);
/* if it's startup and the window is unmapped, don't manage it */
@@ -1428,7 +1437,8 @@ WWindow *wManageInternalWindow(WScreen *scr, Window window, Window owner,
foo = WFF_RIGHT_BUTTON | WFF_BORDER;
foo |= WFF_TITLEBAR;
#ifdef XKB_BUTTON_HINT
foo |= WFF_LANGUAGE_BUTTON;
if (wPreferences.modelock)
foo |= WFF_LANGUAGE_BUTTON;
#endif
wwin->frame = wFrameWindowCreate(scr, WMFloatingLevel,
@@ -2726,20 +2736,6 @@ WMagicNumber wWindowAddSavedState(const char *instance, const char *class,
return wstate;
}
static inline int is_same(const char *x, const char *y)
{
if ((x == NULL) && (y == NULL))
return 1;
if ((x == NULL) || (y == NULL))
return 0;
if (strcmp(x, y) == 0)
return 1;
else
return 0;
}
WMagicNumber wWindowGetSavedState(Window win)
{
char *instance, *class, *command = NULL;
@@ -2754,9 +2750,9 @@ WMagicNumber wWindowGetSavedState(Window win)
if (PropGetWMClass(win, &class, &instance)) {
while (wstate) {
if (is_same(instance, wstate->instance) &&
is_same(class, wstate->class) &&
is_same(command, wstate->command)) {
if (WMStrEqual(instance, wstate->instance) &&
WMStrEqual(class, wstate->class) &&
WMStrEqual(command, wstate->command)) {
break;
}
wstate = wstate->next;
@@ -2982,7 +2978,7 @@ static void frameMouseDown(WObjDescriptor *desc, XEvent *event)
CloseWindowMenu(wwin->screen_ptr);
if (!(event->xbutton.state & ControlMask) && !WFLAGP(wwin, no_focusable))
if (event->xbutton.button != Button4 && event->xbutton.button != Button5 && !(event->xbutton.state & ControlMask) && !WFLAGP(wwin, no_focusable))
wSetFocusTo(wwin->screen_ptr, wwin);
if (event->xbutton.button == Button1)
+6
View File
@@ -237,6 +237,12 @@ typedef struct WWindow {
int cmap_window_no;
Window *cmap_windows;
/* move/resize state for _NET_WM_MOVERESIZE support */
struct {
int active; /* 1 if move/resize is in progress */
int resize_edge; /* which edge for resize (0-7) */
} moveresize;
/* protocols */
WProtocols protocols; /* accepted WM_PROTOCOLS */
+9 -28
View File
@@ -55,7 +55,11 @@
#define UPDATE_TEXT_FIELD 2
#define REVERT_TO_DEFAULT 4
#define PWIDTH 290
#ifdef XKB_BUTTON_HINT
#define PHEIGHT 380
#else
#define PHEIGHT 360
#endif
#define UNDEFINED_POS 0xffffff
#define UPDATE_DEFAULTS 1
#define IS_BOOLEAN 2
@@ -504,33 +508,6 @@ static int showIconFor(WMScreen *scrPtr, InspectorPanel *panel, const char *wm_i
return 0;
}
static int getBool(WMPropList *value)
{
char *val;
if (!WMIsPLString(value))
return 0;
val = WMGetFromPLString(value);
if (val == NULL)
return 0;
if ((val[1] == '\0' &&
(val[0] == 'y' || val[0] == 'Y' || val[0] == 'T' ||
val[0] == 't' || val[0] == '1')) ||
(strcasecmp(val, "YES") == 0 || strcasecmp(val, "TRUE") == 0)) {
return 1;
} else if ((val[1] == '\0' &&
(val[0] == 'n' || val[0] == 'N' || val[0] == 'F' ||
val[0] == 'f' || val[0] == '0')) ||
(strcasecmp(val, "NO") == 0 || strcasecmp(val, "FALSE") == 0)) {
return 0;
} else {
wwarning(_("can't convert \"%s\" to boolean"), val);
return 0;
}
}
/* Will insert the attribute = value; pair in window's list,
* if it's different from the defaults.
* Defaults means either defaults database, or attributes saved
@@ -554,7 +531,7 @@ insertAttribute(WMPropList *dict, WMPropList *window, WMPropList *attr, WMPropLi
def_value = ((flags & IS_BOOLEAN) != 0) ? No : EmptyString;
if (flags & IS_BOOLEAN)
update = (getBool(value) != getBool(def_value));
update = (WMPLGetBool(value) != WMPLGetBool(def_value));
else
update = !WMIsPropListEqualTo(value, def_value);
@@ -1342,7 +1319,11 @@ static void create_tab_window_advanced(WWindow *wwin, InspectorPanel *panel, int
panel->moreFrm = WMCreateFrame(panel->win);
WMSetFrameTitle(panel->moreFrm, _("Advanced"));
WMMoveWidget(panel->moreFrm, 15, 45);
#ifdef XKB_BUTTON_HINT
WMResizeWidget(panel->moreFrm, frame_width, 285);
#else
WMResizeWidget(panel->moreFrm, frame_width, 265);
#endif
for (i = 0; i < wlengthof(advanced_option); i++) {
int is_userdef, flag;
+104 -33
View File
@@ -32,7 +32,9 @@
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <WINGs/WUtil.h>
#include "WindowMaker.h"
@@ -69,7 +71,7 @@ static Atom net_showing_desktop;
/* Other Root Window Messages */
static Atom net_close_window;
static Atom net_moveresize_window; /* TODO */
static Atom net_wm_moveresize; /* TODO */
static Atom net_wm_moveresize;
/* Application Window Properties */
static Atom net_wm_name;
@@ -123,7 +125,7 @@ static Atom net_wm_strut;
static Atom net_wm_strut_partial; /* TODO: doesn't really fit into the current strut scheme */
static Atom net_wm_icon_geometry; /* FIXME: should work together with net_wm_handled_icons, gnome-panel-2.2.0.1 doesn't use _NET_WM_HANDLED_ICONS, thus present situation. */
static Atom net_wm_icon;
static Atom net_wm_pid; /* TODO */
static Atom net_wm_pid;
static Atom net_wm_handled_icons; /* FIXME: see net_wm_icon_geometry */
static Atom net_wm_window_opacity;
@@ -224,35 +226,6 @@ static atomitem_t atomNames[] = {
#define _NET_WM_STATE_ADD 1
#define _NET_WM_STATE_TOGGLE 2
#if 0
/*
* These constant provide information on the kind of window move/resize when
* it is initiated by the application instead of by WindowMaker. They are
* parameter for the client message _NET_WM_MOVERESIZE, as defined by the
* FreeDesktop wm-spec standard:
* http://standards.freedesktop.org/wm-spec/1.5/ar01s04.html
*
* Today, WindowMaker does not support this at all (the corresponding Atom
* is not added to the list in setSupportedHints), probably because there is
* nothing it needs to do about it, the application is assumed to know what
* it is doing, and WindowMaker won't get in the way.
*
* The definition of the constants (taken from the standard) are disabled to
* avoid a spurious warning (-Wunused-macros).
*/
#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0
#define _NET_WM_MOVERESIZE_SIZE_TOP 1
#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2
#define _NET_WM_MOVERESIZE_SIZE_RIGHT 3
#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4
#define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5
#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6
#define _NET_WM_MOVERESIZE_SIZE_LEFT 7
#define _NET_WM_MOVERESIZE_MOVE 8 /* movement only */
#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD 9 /* size via keyboard */
#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 /* move via keyboard */
#endif
static void observer(void *self, WMNotification *notif);
static void wsobserver(void *self, WMNotification *notif);
@@ -274,6 +247,9 @@ static void setSupportedHints(WScreen *scr)
{
Atom atom[wlengthof(atomNames)];
int i = 0;
long pid = 0;
char wm_name[64] = "";
XClassHint *class_hint;
/* set supported hints list */
/* XXX: extend this !!! */
@@ -289,9 +265,7 @@ static void setSupportedHints(WScreen *scr)
atom[i++] = net_workarea;
atom[i++] = net_supporting_wm_check;
atom[i++] = net_showing_desktop;
#if 0
atom[i++] = net_wm_moveresize;
#endif
atom[i++] = net_wm_desktop;
#ifdef USE_XINERAMA
atom[i++] = net_wm_fullscreen_monitors;
@@ -357,6 +331,25 @@ static void setSupportedHints(WScreen *scr)
XChangeProperty(dpy, scr->info_window, net_supporting_wm_check, XA_WINDOW,
32, PropModeReplace, (unsigned char *)&scr->info_window, 1);
/* set _NET_WM_NAME on supporting window */
snprintf(wm_name, sizeof(wm_name), "WindowMaker %s", VERSION);
XChangeProperty(dpy, scr->info_window, net_wm_name, utf8_string, 8,
PropModeReplace, (unsigned char *)wm_name, strlen(wm_name));
/* set _NET_WM_PID on supporting window */
pid = getpid();
XChangeProperty(dpy, scr->info_window, net_wm_pid, XA_CARDINAL, 32,
PropModeReplace, (unsigned char *)&pid, 1);
/* set WM_CLASS on supporting window */
class_hint = XAllocClassHint();
if (class_hint) {
class_hint->res_name = "wmaker";
class_hint->res_class = "WindowMaker";
XSetClassHint(dpy, scr->info_window, class_hint);
XFree(class_hint);
}
}
void wNETWMUpdateDesktop(WScreen *scr)
@@ -1790,6 +1783,84 @@ Bool wNETWMProcessClientMessage(XClientMessageEvent *event)
}
return True;
} else if (event->message_type == net_wm_moveresize) {
XEvent fake_event;
int direction = event->data.l[2];
int x_root = event->data.l[0];
int y_root = event->data.l[1];
int button = event->data.l[3];
int junk;
Window junkw;
unsigned int mask;
if (direction == _NET_WM_MOVERESIZE_CANCEL) {
if (wwin->moveresize.active) {
memset(&fake_event, 0, sizeof(XEvent));
fake_event.type = ButtonRelease;
fake_event.xbutton.window = wwin->frame->core->window;
fake_event.xbutton.x_root = x_root;
fake_event.xbutton.y_root = y_root;
fake_event.xbutton.button = event->data.l[3];
XSendEvent(dpy, wwin->frame->core->window, False, ButtonReleaseMask, &fake_event);
}
return True;
}
/* Check if already in progress */
if (wwin->moveresize.active)
return True;
/* Check if the initiating button is actually pressed */
if (!XQueryPointer(dpy, wwin->screen_ptr->root_win, &junkw, &junkw,
&junk, &junk, &junk, &junk, &mask))
return True;
if (button > 0) {
unsigned int expected =
button == 1 ? Button1Mask :
button == 2 ? Button2Mask :
button == 3 ? Button3Mask :
0;
if (expected && !(mask & expected))
/* Race: button already released */
return True;
}
/* Check if operation is allowed */
if (direction == _NET_WM_MOVERESIZE_MOVE) {
if (WFLAGP(wwin, no_movable))
return True;
} else if (direction <= _NET_WM_MOVERESIZE_SIZE_LEFT) {
if (WFLAGP(wwin, no_resizable))
return True;
} else {
return True;
}
/* Grab the pointer for the operation */
if (XGrabPointer(dpy, wwin->frame->core->window, False,
ButtonReleaseMask | PointerMotionMask | ButtonPressMask,
GrabModeAsync, GrabModeAsync,
None, None, CurrentTime) != GrabSuccess)
return True;
/* Set up the move/resize state */
wwin->moveresize.active = 1;
wwin->moveresize.resize_edge = direction;
memset(&fake_event, 0, sizeof(XEvent));
fake_event.type = MotionNotify;
fake_event.xmotion.x_root = x_root;
fake_event.xmotion.y_root = y_root;
fake_event.xmotion.window = wwin->frame->core->window;
if (direction == _NET_WM_MOVERESIZE_MOVE)
wMouseMoveWindow(wwin, &fake_event);
else
wMouseResizeWindow(wwin, &fake_event);
return True;
#ifdef USE_XINERAMA
} else if (event->message_type == net_wm_fullscreen_monitors) {
unsigned long top, bottom, left, right, src_indication;
+20
View File
@@ -28,6 +28,26 @@
#include "window.h"
#include <X11/Xlib.h>
/*
* These constant provide information on the kind of window move/resize when
* it is initiated by the application instead of by WindowMaker. They are
* parameter for the client message _NET_WM_MOVERESIZE, as defined by the
* FreeDesktop wm-spec standard:
* http://standards.freedesktop.org/wm-spec/1.5/ar01s04.html
*/
#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0
#define _NET_WM_MOVERESIZE_SIZE_TOP 1
#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2
#define _NET_WM_MOVERESIZE_SIZE_RIGHT 3
#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4
#define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5
#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6
#define _NET_WM_MOVERESIZE_SIZE_LEFT 7
#define _NET_WM_MOVERESIZE_MOVE 8 /* movement only */
#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD 9 /* size via keyboard */
#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 /* move via keyboard */
#define _NET_WM_MOVERESIZE_CANCEL 11 /* cancel operation */
void wNETWMInitStuff(WScreen *scr);
void wNETWMCleanup(WScreen *scr);
void wNETWMUpdateWorkarea(WScreen *scr);
+3 -2
View File
@@ -76,9 +76,10 @@ wmiv_LDADD = \
$(top_builddir)/wrlib/libwraster.la \
$(top_builddir)/WINGs/libWINGs.la \
@XLFLAGS@ @XLIBS@ @GFXLIBS@ \
@PANGO_LIBS@ @PTHREAD_LIBS@ @LIBEXIF@
@PANGO_LIBS@ @PTHREAD_LIBS@ @LIBEXIF@ \
@LIBARCHIVE_LIBS@ @LIBM@ @LIBXRANDR@
wmiv_SOURCES = wmiv.c wmiv.h
wmiv_SOURCES = wmiv.c wmiv.hi xdnd.c xdnd.h
CLEANFILES = wmaker.inst
+1 -1
View File
@@ -2,7 +2,7 @@ File Language Note Current Maintainer
------------------------------------------------------------------------------
nl.po Dutch Alwin <translations@ziggo.nl>
fy.po Frisian Alwin <translations@ziggo.nl>
sr.po Serbian Strahinya Radich <contact@strahinja.org>
sr.po Serbian Strahinya Radich <sr@strahinja.org>
Notes
-----
+2 -2
View File
@@ -3,7 +3,7 @@
# 1998-2006 Dan Pascu
# 2013-2020 Window Maker Developers Team
# This file is distributed under the same license as the Window Maker package.
# Strahinya Radich (Страхиња Радић) <contact@strahinja.org>, 2023.
# Strahinya Radich (Страхиња Радић) <sr@strahinja.org>, 2023.
#
msgid ""
msgstr ""
@@ -11,7 +11,7 @@ msgstr ""
"Report-Msgid-Bugs-To: wmaker-dev@googlegroups.com\n"
"POT-Creation-Date: 2023-11-02 19:43+0100\n"
"PO-Revision-Date: 2023-11-02 19:48+0100\n"
"Last-Translator: Страхиња Радић <contact@strahinja.org>\n"
"Last-Translator: Страхиња Радић <sr@strahinja.org>\n"
"Language-Team: Serbian <sr@li.org>\n"
"Language: sr\n"
"MIME-Version: 1.0\n"
+1494 -229
View File
File diff suppressed because it is too large Load Diff
+1596
View File
File diff suppressed because it is too large Load Diff
+219
View File
@@ -0,0 +1,219 @@
/* xdnd.c, xdnd.h - C program library for handling the Xdnd protocol
Copyright (C) 1996-2000 Paul Sheer
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307, USA.
*/
#ifndef _X_DND_H
#define _X_DND_H
#ifdef __cplusplus
extern "C" {
#endif
/* you can set this to either 2 (which support 0 and 1 as well) or 3 */
/* #define XDND_VERSION 2 */
#define XDND_VERSION 3
/* XdndEnter */
#define XDND_THREE 3
#define XDND_ENTER_SOURCE_WIN(e) ((e)->xclient.data.l[0])
#define XDND_ENTER_THREE_TYPES(e) (((e)->xclient.data.l[1] & 0x1UL) == 0)
#define XDND_ENTER_THREE_TYPES_SET(e,b) (e)->xclient.data.l[1] = ((e)->xclient.data.l[1] & ~0x1UL) | (((b) == 0) ? 0 : 0x1UL)
#define XDND_ENTER_VERSION(e) ((e)->xclient.data.l[1] >> 24)
#define XDND_ENTER_VERSION_SET(e,v) (e)->xclient.data.l[1] = ((e)->xclient.data.l[1] & ~(0xFF << 24)) | ((v) << 24)
#define XDND_ENTER_TYPE(e,i) ((e)->xclient.data.l[2 + i]) /* i => (0, 1, 2) */
/* XdndPosition */
#define XDND_POSITION_SOURCE_WIN(e) ((e)->xclient.data.l[0])
#define XDND_POSITION_ROOT_X(e) ((e)->xclient.data.l[2] >> 16)
#define XDND_POSITION_ROOT_Y(e) ((e)->xclient.data.l[2] & 0xFFFFUL)
#define XDND_POSITION_ROOT_SET(e,x,y) (e)->xclient.data.l[2] = ((x) << 16) | ((y) & 0xFFFFUL)
#define XDND_POSITION_TIME(e) ((e)->xclient.data.l[3])
#define XDND_POSITION_ACTION(e) ((e)->xclient.data.l[4])
/* XdndStatus */
#define XDND_STATUS_TARGET_WIN(e) ((e)->xclient.data.l[0])
#define XDND_STATUS_WILL_ACCEPT(e) ((e)->xclient.data.l[1] & 0x1L)
#define XDND_STATUS_WILL_ACCEPT_SET(e,b) (e)->xclient.data.l[1] = ((e)->xclient.data.l[1] & ~0x1UL) | (((b) == 0) ? 0 : 0x1UL)
#define XDND_STATUS_WANT_POSITION(e) ((e)->xclient.data.l[1] & 0x2UL)
#define XDND_STATUS_WANT_POSITION_SET(e,b) (e)->xclient.data.l[1] = ((e)->xclient.data.l[1] & ~0x2UL) | (((b) == 0) ? 0 : 0x2UL)
#define XDND_STATUS_RECT_X(e) ((e)->xclient.data.l[2] >> 16)
#define XDND_STATUS_RECT_Y(e) ((e)->xclient.data.l[2] & 0xFFFFL)
#define XDND_STATUS_RECT_WIDTH(e) ((e)->xclient.data.l[3] >> 16)
#define XDND_STATUS_RECT_HEIGHT(e) ((e)->xclient.data.l[3] & 0xFFFFL)
#define XDND_STATUS_RECT_SET(e,x,y,w,h) {(e)->xclient.data.l[2] = ((x) << 16) | ((y) & 0xFFFFUL); (e)->xclient.data.l[3] = ((w) << 16) | ((h) & 0xFFFFUL); }
#define XDND_STATUS_ACTION(e) ((e)->xclient.data.l[4])
/* XdndLeave */
#define XDND_LEAVE_SOURCE_WIN(e) ((e)->xclient.data.l[0])
/* XdndDrop */
#define XDND_DROP_SOURCE_WIN(e) ((e)->xclient.data.l[0])
#define XDND_DROP_TIME(e) ((e)->xclient.data.l[2])
/* XdndFinished */
#define XDND_FINISHED_TARGET_WIN(e) ((e)->xclient.data.l[0])
struct _DndCursor {
int width, height;
int x, y;
unsigned char *image_data, *mask_data;
char *_action;
Pixmap image_pixmap, mask_pixmap;
Cursor cursor;
Atom action;
};
typedef struct _DndCursor DndCursor;
typedef struct _DndClass DndClass;
struct _DndClass {
/* insert chars sequentionally into the target widget, type will be the same as `desired_type'
returned from widget_apply_position. This may be called several times in succession
with sequention blocks of data. Must return non-zero on failure */
int (*widget_insert_drop) (DndClass * dnd, unsigned char *data, int length, int remaining, Window into, Window from, Atom type);
/* In response to DELETE requests : FIXME - not yet used */
int (*widget_delete_selection) (DndClass * dnd, Window window, Window from);
/* returns 1 if widget exists, zero otherwise. If this method is not
set then the code assumes that no widgets have support for recieving drops.
In this case none of the widget methods need be set. */
int (*widget_exists) (DndClass * dnd, Window window);
/* must update the widgets border to its default appearance */
void (*widget_apply_leave) (DndClass * dnd, Window widgets_window);
/* must update the widgets border to give the appearance of being able to recieve a drop,
plus return all data to pointers. As per the protocol, if the widget cannot
perform the action specified by `action' then it should return either XdndActionPrivate
or XdndActionCopy into supported_action (leaving 0 supported_action unchanged is equivalent
to XdndActionCopy). Returns 1 if ready to ok drop */
int (*widget_apply_position) (DndClass * dnd, Window widgets_window, Window from,
Atom action, int x, int y, Time t, Atom * typelist,
int *want_position, Atom * supported_action, Atom * desired_type,
XRectangle * rectangle);
/* returns drag data of the specified type. This will be one of `typelist' given to xdnd_drag */
void (*widget_get_data) (DndClass * dnd, Window window, unsigned char **data, int *length, Atom type);
/* this is called from with the main event loop if an expose event is recieved and is optional */
void (*handle_expose_events) (DndClass * dnd, XEvent * xevent);
/* creates a chooser dialog if the action is XdndActionAsk. Returns non-zero on cancel */
int (*action_choose_dialog) (DndClass * dnd, char **descriptions, Atom * actions, Atom * result);
#if 0 /* implemented internally */
/* returns a widget that is dnd aware within a parent widget that lies under the point x, y */
Window (*widget_get_child_widget) (DndClass * dnd, Window parent, int x, int y);
#endif
void *pad1[8];
DndCursor *cursors;
Display *display;
Atom XdndAware;
Atom XdndSelection;
Atom XdndEnter;
Atom XdndLeave;
Atom XdndPosition;
Atom XdndDrop;
Atom XdndFinished;
Atom XdndStatus;
Atom XdndActionCopy;
Atom XdndActionMove;
Atom XdndActionLink;
Atom XdndActionAsk;
Atom XdndActionPrivate;
Atom XdndTypeList;
Atom XdndActionList;
Atom XdndActionDescription;
Atom Xdnd_NON_PROTOCOL_ATOM;
Atom version;
Atom pad2[16];
Window root_window;
#define XDND_DROP_STAGE_IDLE 0
#define XDND_DRAG_STAGE_DRAGGING 1
#define XDND_DRAG_STAGE_ENTERED 2
#define XDND_DROP_STAGE_CONVERTING 3
#define XDND_DROP_STAGE_ENTERED 4
int stage;
int dragging_version;
int internal_drag;
int want_position;
int ready_to_drop;
int will_accept;
XRectangle rectangle;
Window dropper_window, dragger_window;
Atom *dragger_typelist;
Atom desired_type;
Atom supported_action;
Time time;
/* drop position from last XdndPosition */
int x, y;
int pad3[16];
/* move euclidian pixels before considering this to be an actual drag */
float drag_threshold;
/* block for only this many seconds on not receiving a XdndFinished from target, default : 10 */
int time_out;
#define XDND_OPTION_NO_HYSTERESIS (1<<0)
int options;
/* user hooks */
void *user_hook1;
void *user_hook2;
void *user_hook3;
Window dropper_toplevel;
void *pad4[15];
};
void xdnd_init (DndClass * dnd, Display * display);
void xdnd_shut (DndClass * dnd);
/* for nested widgets where parent and child receive drops of different
types; then always pass typelist as null */
void xdnd_set_dnd_aware (DndClass * dnd, Window window, Atom * typelist);
int xdnd_is_dnd_aware (DndClass * dnd, Window window, int *version, Atom * typelist);
void xdnd_set_type_list (DndClass * dnd, Window window, Atom * typelist);
void xdnd_set_actions (DndClass * dnd, Window window, Atom * actions, char **descriptions);
int xdnd_get_actions (DndClass * dnd, Window window, Atom ** actions, char ***descriptions);
int xdnd_choose_action_dialog (DndClass * dnd, Atom * actions, char **descriptions, Atom * result);
Atom xdnd_drag (DndClass * dnd, Window from, Atom action, Atom * typelist);
/* Returns 1 if event is handled, This must be placed in the widget
libraries main event loop and be called if the event type is
ClientMessage or SelectionNotify */
int xdnd_handle_drop_events (DndClass * dnd, XEvent * xevent);
Atom xdnd_get_drop (Display * display, XEvent * xevent, Atom * typelist, Atom * actionlist,
unsigned char **data, int *length, Atom * type, int *x, int *y);
#ifdef __cplusplus
}
#endif
#endif /* !_X_DND_H */
+2
View File
@@ -1,3 +1,5 @@
- added RSaveRawImage()
- added RSaveTitledImage()
- removed obsoleted RDestroyImage()
+4
View File
@@ -58,6 +58,10 @@ libwraster_la_SOURCES += load_jpeg.c
libwraster_la_SOURCES += save_jpeg.c
endif
if USE_JXL
libwraster_la_SOURCES += load_jxl.c
endif
if USE_PNG
libwraster_la_SOURCES += load_png.c
libwraster_la_SOURCES += save_png.c
+38 -38
View File
@@ -25,47 +25,47 @@
void RCombineAlpha(unsigned char *d, unsigned char *s, int s_has_alpha,
int width, int height, int dwi, int swi, int opacity) {
int x, y;
int t, sa;
int alpha;
float ratio, cratio;
int width, int height, int dwi, int swi, int opacity) {
int x, y;
unsigned char *dst = d;
unsigned char *src = s;
for (y=0; y<height; y++) {
for (x=0; x<width; x++) {
sa=s_has_alpha?*(s+3):255;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
int sa = s_has_alpha ? src[3] : 255;
int t, alpha;
if (opacity!=255) {
t = sa * opacity + 0x80;
sa = ((t>>8)+t)>>8;
}
if (opacity != 255) {
t = sa * opacity + 0x80;
sa = ((t >> 8) + t) >> 8;
}
t = *(d+3) * (255-sa) + 0x80;
alpha = sa + (((t>>8)+t)>>8);
t = dst[3] * (255 - sa) + 0x80;
alpha = sa + (((t >> 8) + t) >> 8);
if (sa==0 || alpha==0) {
ratio = 0;
cratio = 1.0;
} else if(sa == alpha) {
ratio = 1.0;
cratio = 0;
} else {
ratio = (float)sa / alpha;
cratio = 1.0F - ratio;
}
if (alpha == 0) {
dst[3] = 0;
} else if (sa == alpha) {
dst[0] = src[0];
dst[1] = src[1];
dst[2] = src[2];
dst[3] = alpha;
} else if (sa == 0) {
dst[3] = alpha;
} else {
int ratio = (sa << 8) / alpha;
int inv_ratio = 256 - ratio;
*d = (int)*d * cratio + (int)*s * ratio;
s++; d++;
*d = (int)*d * cratio + (int)*s * ratio;
s++; d++;
*d = (int)*d * cratio + (int)*s * ratio;
s++; d++;
*d = alpha;
d++;
dst[0] = (dst[0] * inv_ratio + src[0] * ratio) >> 8;
dst[1] = (dst[1] * inv_ratio + src[1] * ratio) >> 8;
dst[2] = (dst[2] * inv_ratio + src[2] * ratio) >> 8;
dst[3] = alpha;
}
if (s_has_alpha) s++;
}
d+=dwi;
s+=swi;
}
}
dst += 4;
src += s_has_alpha ? 4 : 3;
}
dst += dwi;
src += swi;
}
}
+1 -1
View File
@@ -554,7 +554,7 @@ RContext *RCreateContext(Display * dpy, int screen_number, const RContextAttribu
if (!(context->attribs->flags & RC_ScalingFilter)) {
context->attribs->flags |= RC_ScalingFilter;
context->attribs->scaling_filter = RMitchellFilter;
context->attribs->scaling_filter = RCatmullRomFilter;
}
/* get configuration from environment variables */
+9 -2
View File
@@ -38,12 +38,13 @@ typedef enum {
IM_PPM = 4,
IM_JPEG = 5,
IM_GIF = 6,
IM_WEBP = 7
IM_WEBP = 7,
IM_JXL = 8
} WRImgFormat;
/* How many image types we have. */
/* Increase this when adding new image types! */
#define IM_TYPES 7
#define IM_TYPES 8
/*
* Function for Loading in a specific format
@@ -64,6 +65,10 @@ RImage *RLoadPNG(RContext *context, const char *file);
RImage *RLoadJPEG(const char *file);
#endif
#ifdef USE_JXL
RImage *RLoadJXL(const char *file);
#endif
#ifdef USE_GIF
RImage *RLoadGIF(const char *file, int index);
#endif
@@ -85,10 +90,12 @@ Bool RSaveXPM(RImage *image, const char *filename);
#ifdef USE_PNG
Bool RSavePNG(RImage *image, const char *filename, char *title);
Bool RSaveRawPNG(RImage *image, char *title, unsigned char **out_buf, size_t *out_size);
#endif
#ifdef USE_JPEG
Bool RSaveJPEG(RImage *image, const char *filename, char *title);
Bool RSaveRawJPEG(RImage *image, char *title, unsigned char **out_buf, size_t *out_size);
#endif
/*
+27 -1
View File
@@ -3,7 +3,7 @@
* Raster graphics library
*
* Copyright (c) 1997-2003 Alfredo K. Kojima
* Copyright (c) 2014-2021 Window Maker Team
* Copyright (c) 2014-2025 Window Maker Team
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -93,6 +93,9 @@ char **RSupportedFileFormats(void)
#ifdef USE_JPEG
tmp[i++] = "JPEG";
#endif
#ifdef USE_JXL
tmp[i++] = "JXL";
#endif
#ifdef USE_GIF
tmp[i++] = "GIF";
#endif
@@ -159,6 +162,11 @@ RImage *RLoadImage(RContext *context, const char *file, int index)
assert(file != NULL);
/* just to suppress the compilation warning as index is only used with TIFF and GIF */
#if !defined(USE_TIFF) && !defined(USE_GIF)
(void)index;
#endif
if (RImageCacheSize < 0)
init_cache();
@@ -219,6 +227,12 @@ RImage *RLoadImage(RContext *context, const char *file, int index)
break;
#endif /* USE_JPEG */
#ifdef USE_JXL
case IM_JXL:
image = RLoadJXL(file);
break;
#endif /* USE_JXL */
#ifdef USE_GIF
case IM_GIF:
image = RLoadGIF(file, index);
@@ -305,6 +319,11 @@ char *RGetImageFileFormat(const char *file)
return "JPEG";
#endif /* USE_JPEG */
#ifdef USE_JXL
case IM_JXL:
return "JXL";
#endif /* USE_JXL */
#ifdef USE_GIF
case IM_GIF:
return "GIF";
@@ -377,6 +396,13 @@ static WRImgFormat identFile(const char *path)
if (buffer[0] == 0xff && buffer[1] == 0xd8)
return IM_JPEG;
/* check for JXL */
if ((buffer[0] == 0xff && buffer[1] == 0x0a) || /* naked codestream */
(buffer[0] == 0x00 && buffer[1] == 0x00 && buffer[2] == 0x00 && buffer[3] == 0x0c && /* container format */
buffer[4] == 0x4a && buffer[5] == 0x58 && buffer[6] == 0x4c && buffer[7] == 0x20 &&
buffer[8] == 0x0d && buffer[9] == 0x0a && buffer[10] == 0x87 && buffer[11] == 0x0a))
return IM_JXL;
/* check for GIF */
if (buffer[0] == 'G' && buffer[1] == 'I' && buffer[2] == 'F' && buffer[3] == '8' &&
(buffer[4] == '7' || buffer[4] == '9') && buffer[5] == 'a')
+211
View File
@@ -0,0 +1,211 @@
/* load_jxl.c - load JXL (JPEG XL) image from file
*
* Raster graphics library
*
* Copyright (c) 2025 Window Maker Team
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <config.h>
#ifdef USE_JXL
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <jxl/decode.h>
#include "wraster.h"
#include "imgformat.h"
#include "wr_i18n.h"
static unsigned char *do_read_file(const char *filename, size_t *size)
{
FILE *file;
struct stat st;
unsigned char *data;
if (stat(filename, &st) != 0) {
RErrorCode = RERR_OPEN;
return NULL;
}
file = fopen(filename, "rb");
if (!file) {
RErrorCode = RERR_OPEN;
return NULL;
}
*size = st.st_size;
data = malloc(*size);
if (!data) {
RErrorCode = RERR_NOMEMORY;
fclose(file);
return NULL;
}
if (fread(data, 1, *size, file) != *size) {
RErrorCode = RERR_READ;
free(data);
fclose(file);
return NULL;
}
fclose(file);
return data;
}
RImage *RLoadJXL(const char *file)
{
RImage *image = NULL;
unsigned char *data = NULL, *pixels = NULL;
size_t size;
JxlDecoder *dec = NULL;
JxlDecoderStatus status;
JxlBasicInfo info;
JxlPixelFormat format;
size_t buffer_size;
int width = 0, height = 0;
int has_alpha = 0;
/* Load file data */
data = do_read_file(file, &size);
if (!data)
return NULL;
/* Create decoder */
dec = JxlDecoderCreate(NULL);
if (!dec) {
RErrorCode = RERR_NOMEMORY;
goto error;
}
/* Subscribe to basic info and full image */
if (JxlDecoderSubscribeEvents(dec, JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE) != JXL_DEC_SUCCESS) {
RErrorCode = RERR_BADIMAGEFILE;
goto error;
}
/* Set input data */
if (JxlDecoderSetInput(dec, data, size) != JXL_DEC_SUCCESS) {
RErrorCode = RERR_BADIMAGEFILE;
goto error;
}
/* Process events */
for (;;) {
status = JxlDecoderProcessInput(dec);
if (status == JXL_DEC_ERROR) {
RErrorCode = RERR_BADIMAGEFILE;
goto error;
}
if (status == JXL_DEC_NEED_MORE_INPUT) {
RErrorCode = RERR_BADIMAGEFILE;
goto error;
}
if (status == JXL_DEC_BASIC_INFO) {
if (JxlDecoderGetBasicInfo(dec, &info) != JXL_DEC_SUCCESS) {
RErrorCode = RERR_BADIMAGEFILE;
goto error;
}
width = info.xsize;
height = info.ysize;
if (width < 1 || height < 1) {
RErrorCode = RERR_BADIMAGEFILE;
goto error;
}
/* Check if image has alpha channel */
has_alpha = (info.alpha_bits > 0);
/* Set pixel format based on alpha channel presence */
if (has_alpha) {
format.num_channels = 4; /* RGBA */
} else {
format.num_channels = 3; /* RGB */
}
format.data_type = JXL_TYPE_UINT8;
format.endianness = JXL_NATIVE_ENDIAN;
format.align = 0;
} else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) {
/* Allocate image with or without alpha */
image = RCreateImage(width, height, has_alpha ? True : False);
if (!image) {
RErrorCode = RERR_NOMEMORY;
goto error;
}
/* Determine buffer size */
if (JxlDecoderImageOutBufferSize(dec, &format, &buffer_size) != JXL_DEC_SUCCESS) {
RErrorCode = RERR_BADIMAGEFILE;
goto error;
}
/* Allocate buffer */
pixels = malloc(buffer_size);
if (!pixels) {
RErrorCode = RERR_NOMEMORY;
goto error;
}
/* Set output buffer */
if (JxlDecoderSetImageOutBuffer(dec, &format, pixels, buffer_size) != JXL_DEC_SUCCESS) {
RErrorCode = RERR_BADIMAGEFILE;
goto error;
}
} else if (status == JXL_DEC_FULL_IMAGE) {
/* Image is ready, copy data directly for RGB or RGBA */
if (has_alpha) {
/* RGBA format - copy directly */
memcpy(image->data, pixels, width * height * 4);
} else {
/* RGB format - copy directly */
memcpy(image->data, pixels, width * height * 3);
}
break;
} else if (status == JXL_DEC_SUCCESS) {
/* All done */
break;
}
}
free(data);
free(pixels);
JxlDecoderDestroy(dec);
return image;
error:
if (data)
free(data);
if (pixels)
free(pixels);
if (image)
RReleaseImage(image);
if (dec)
JxlDecoderDestroy(dec);
return NULL;
}
#endif /* USE_JXL */
+1
View File
@@ -29,6 +29,7 @@ POTFILES = \
$(top_srcdir)/wrlib/load_ppm.c \
$(top_srcdir)/wrlib/load_gif.c \
$(top_srcdir)/wrlib/load_jpeg.c \
$(top_srcdir)/wrlib/load_jxl.c \
$(top_srcdir)/wrlib/load_png.c \
$(top_srcdir)/wrlib/load_tiff.c \
$(top_srcdir)/wrlib/load_xpm.c \
+2 -2
View File
@@ -3,7 +3,7 @@
# 1998-2006 Dan Pascu
# 2013-2020 Window Maker Developers Team
# This file is distributed under the same license as the Window Maker package.
# Strahinya Radich (Страхиња Радић) <contact@strahinja.org>, 2023.
# Strahinya Radich (Страхиња Радић) <sr@strahinja.org>, 2023.
#
msgid ""
msgstr ""
@@ -11,7 +11,7 @@ msgstr ""
"Report-Msgid-Bugs-To: wmaker-dev@googlegroups.com\n"
"POT-Creation-Date: 2023-11-02 19:43+0100\n"
"PO-Revision-Date: 2023-11-02 19:48+0100\n"
"Last-Translator: Страхиња Радић <contact@strahinja.org>\n"
"Last-Translator: Страхиња Радић <sr@strahinja.org>\n"
"Language-Team: Serbian <sr@li.org>\n"
"Language: sr\n"
"MIME-Version: 1.0\n"
+20 -1
View File
@@ -3,7 +3,7 @@
* Raster graphics library
*
* Copyright (c) 1998-2003 Alfredo K. Kojima
* Copyright (c) 2013-2023 Window Maker Team
* Copyright (c) 2013-2025 Window Maker Team
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -40,6 +40,25 @@ Bool RSaveImage(RImage *image, const char *filename, const char *format)
return RSaveTitledImage(image, filename, format, NULL);
}
Bool RSaveRawImage(RImage *image, const char *format, unsigned char **out_buf, size_t *out_size)
{
#ifdef USE_PNG
if (strcasecmp(format, "PNG") == 0)
return RSaveRawPNG(image, NULL, out_buf, out_size);
#endif
#ifdef USE_JPEG
if (strcasecmp(format, "JPG") == 0)
return RSaveRawJPEG(image, NULL, out_buf, out_size);
if (strcasecmp(format, "JPEG") == 0)
return RSaveRawJPEG(image, NULL, out_buf, out_size);
#endif
RErrorCode = RERR_BADFORMAT;
return False;
}
Bool RSaveTitledImage(RImage *image, const char *filename, const char *format, char *title)
{
#ifdef USE_PNG
+120 -21
View File
@@ -2,7 +2,7 @@
*
* Raster graphics library
*
* Copyright (c) 2023 Window Maker Team
* Copyright (c) 2023-2025 Window Maker Team
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -33,39 +33,92 @@
#include "imgformat.h"
#include "wr_i18n.h"
/* Structure for JPEG memory destination */
struct jpeg_mem_data {
unsigned char **out_buf;
size_t *out_size;
unsigned char *buffer;
size_t buffer_size;
};
/* JPEG memory destination methods */
static void jpeg_init_mem_destination(j_compress_ptr cinfo)
{
struct jpeg_mem_data *dest = (struct jpeg_mem_data *)cinfo->client_data;
dest->buffer_size = 32768; /* Initial buffer size */
dest->buffer = malloc(dest->buffer_size);
if (!dest->buffer) {
/* Memory allocation failed - will be caught by caller */
dest->buffer_size = 0;
return;
}
cinfo->dest->next_output_byte = dest->buffer;
cinfo->dest->free_in_buffer = dest->buffer_size;
}
static boolean jpeg_empty_mem_output_buffer(j_compress_ptr cinfo)
{
struct jpeg_mem_data *dest = (struct jpeg_mem_data *)cinfo->client_data;
size_t old_size = dest->buffer_size;
dest->buffer_size *= 2;
dest->buffer = realloc(dest->buffer, dest->buffer_size);
if (!dest->buffer) {
/* Memory allocation failed - signal error */
dest->buffer_size = 0;
return FALSE;
}
cinfo->dest->next_output_byte = dest->buffer + old_size;
cinfo->dest->free_in_buffer = dest->buffer_size - old_size;
return TRUE;
}
static void jpeg_term_mem_destination(j_compress_ptr cinfo)
{
struct jpeg_mem_data *dest = (struct jpeg_mem_data *)cinfo->client_data;
*dest->out_size = dest->buffer_size - cinfo->dest->free_in_buffer;
*dest->out_buf = dest->buffer;
/* Don't free dest->buffer here - caller will free it */
}
/*
* Save RImage to JPEG image
* Save RImage to JPEG data in memory
*/
Bool RSaveJPEG(RImage *img, const char *filename, char *title)
Bool RSaveRawJPEG(RImage *img, char *title, unsigned char **out_buf, size_t *out_size)
{
FILE *file;
int x, y, img_depth;
int x, y;
char *buffer;
RColor pixel;
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
struct jpeg_destination_mgr jpeg_dest;
struct jpeg_mem_data mem_data;
JSAMPROW row_pointer;
file = fopen(filename, "wb");
if (!file) {
RErrorCode = RERR_OPEN;
return False;
}
if (img->format == RRGBAFormat)
img_depth = 4;
else
img_depth = 3;
*out_buf = NULL;
*out_size = 0;
/* collect separate RGB values to a buffer */
buffer = malloc(sizeof(char) * 3 * img->width * img->height);
if (!buffer) {
RErrorCode = RERR_NOMEMORY;
return False;
}
for (y = 0; y < img->height; y++) {
for (x = 0; x < img->width; x++) {
RGetPixel(img, x, y, &pixel);
buffer[y*img->width*3+x*3+0] = (char)(pixel.red);
buffer[y*img->width*3+x*3+1] = (char)(pixel.green);
buffer[y*img->width*3+x*3+2] = (char)(pixel.blue);
/* Handle transparent pixels by converting them to white
* since JPEG doesn't support transparency */
if (pixel.alpha == 0) {
buffer[y*img->width*3+x*3+0] = (char)255; /* white red */
buffer[y*img->width*3+x*3+1] = (char)255; /* white green */
buffer[y*img->width*3+x*3+2] = (char)255; /* white blue */
} else {
buffer[y*img->width*3+x*3+0] = (char)(pixel.red);
buffer[y*img->width*3+x*3+1] = (char)(pixel.green);
buffer[y*img->width*3+x*3+2] = (char)(pixel.blue);
}
}
}
@@ -74,7 +127,17 @@ Bool RSaveJPEG(RImage *img, const char *filename, char *title)
/* Initialize cinfo structure */
jpeg_create_compress(&cinfo);
jpeg_stdio_dest(&cinfo, file);
/* Set up custom memory destination */
mem_data.out_buf = out_buf;
mem_data.out_size = out_size;
jpeg_dest.init_destination = jpeg_init_mem_destination;
jpeg_dest.empty_output_buffer = jpeg_empty_mem_output_buffer;
jpeg_dest.term_destination = jpeg_term_mem_destination;
cinfo.dest = &jpeg_dest;
cinfo.dest->next_output_byte = NULL;
cinfo.dest->free_in_buffer = 0;
cinfo.client_data = &mem_data;
cinfo.image_width = img->width;
cinfo.image_height = img->height;
@@ -90,15 +153,51 @@ Bool RSaveJPEG(RImage *img, const char *filename, char *title)
jpeg_write_marker(&cinfo, JPEG_COM, (const JOCTET*)title, strlen(title));
while (cinfo.next_scanline < cinfo.image_height) {
row_pointer = (JSAMPROW) &buffer[cinfo.next_scanline * img_depth * img->width];
row_pointer = (JSAMPROW) &buffer[cinfo.next_scanline * 3 * img->width];
jpeg_write_scanlines(&cinfo, &row_pointer, 1);
}
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
/* Clean */
free(buffer);
fclose(file);
return True;
}
/*
* Save RImage to JPEG file
*/
Bool RSaveJPEG(RImage *img, const char *filename, char *title)
{
FILE *file;
unsigned char *jpeg_data;
size_t jpeg_size;
size_t written;
/* Generate JPEG data in memory */
if (!RSaveRawJPEG(img, title, &jpeg_data, &jpeg_size)) {
return False;
}
/* Write to file */
file = fopen(filename, "wb");
if (!file) {
free(jpeg_data);
RErrorCode = RERR_OPEN;
return False;
}
written = fwrite(jpeg_data, 1, jpeg_size, file);
fclose(file);
free(jpeg_data);
if (written != jpeg_size) {
RErrorCode = RERR_WRITE;
return False;
}
return True;
}
+112 -18
View File
@@ -2,7 +2,7 @@
*
* Raster graphics library
*
* Copyright (c) 2023 Window Maker Team
* Copyright (c) 2023-2025 Window Maker Team
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -33,12 +33,51 @@
#include "imgformat.h"
#include "wr_i18n.h"
/*
* Save RImage to PNG image
*/
Bool RSavePNG(RImage *img, const char *filename, char *title)
/* Structure to hold PNG data in memory */
struct png_mem_data {
unsigned char *buffer;
size_t size;
size_t capacity;
};
/* Callback function to write PNG data to memory buffer */
static void png_write_to_memory(png_structp png_ptr, png_bytep data, png_size_t length)
{
struct png_mem_data *p = (struct png_mem_data *)png_get_io_ptr(png_ptr);
size_t new_size = p->size + length;
/* Expand buffer if necessary */
if (new_size > p->capacity) {
size_t new_capacity = p->capacity ? p->capacity * 2 : 8192;
while (new_capacity < new_size)
new_capacity *= 2;
unsigned char *new_buffer = realloc(p->buffer, new_capacity);
if (!new_buffer) {
png_error(png_ptr, "Out of memory");
return;
}
p->buffer = new_buffer;
p->capacity = new_capacity;
}
/* Copy data to buffer */
memcpy(p->buffer + p->size, data, length);
p->size += length;
}
/* Dummy flush function for memory I/O */
static void png_flush_memory(png_structp png_ptr)
{
/* No-op for memory I/O */
(void)png_ptr;
}
/*
* Save RImage to PNG data in memory
*/
Bool RSaveRawPNG(RImage *img, char *title, unsigned char **out_buf, size_t *out_size)
{
FILE *file;
png_structp png_ptr;
png_infop png_info_ptr;
png_bytep png_row;
@@ -46,17 +85,14 @@ Bool RSavePNG(RImage *img, const char *filename, char *title)
int x, y;
int width = img->width;
int height = img->height;
struct png_mem_data png_data = {NULL, 0, 0};
file = fopen(filename, "wb");
if (file == NULL) {
RErrorCode = RERR_OPEN;
return False;
}
*out_buf = NULL;
*out_size = 0;
/* Initialize write structure */
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL) {
fclose(file);
RErrorCode = RERR_NOMEMORY;
return False;
}
@@ -64,19 +100,22 @@ Bool RSavePNG(RImage *img, const char *filename, char *title)
/* Initialize info structure */
png_info_ptr = png_create_info_struct(png_ptr);
if (png_info_ptr == NULL) {
fclose(file);
png_destroy_write_struct(&png_ptr, NULL);
RErrorCode = RERR_NOMEMORY;
return False;
}
/* Setup Exception handling */
if (setjmp(png_jmpbuf (png_ptr))) {
fclose(file);
if (setjmp(png_jmpbuf(png_ptr))) {
if (png_data.buffer)
free(png_data.buffer);
png_destroy_write_struct(&png_ptr, &png_info_ptr);
RErrorCode = RERR_INTERNAL;
return False;
}
png_init_io(png_ptr, file);
/* Set up memory I/O */
png_set_write_fn(png_ptr, &png_data, png_write_to_memory, png_flush_memory);
/* Write header (8 bit colour depth) */
png_set_IHDR(png_ptr, png_info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB,
@@ -96,6 +135,13 @@ Bool RSavePNG(RImage *img, const char *filename, char *title)
/* Allocate memory for one row (3 bytes per pixel - RGB) */
png_row = (png_bytep) malloc(3 * width * sizeof(png_byte));
if (!png_row) {
if (png_data.buffer)
free(png_data.buffer);
png_destroy_write_struct(&png_ptr, &png_info_ptr);
RErrorCode = RERR_NOMEMORY;
return False;
}
/* Write image data */
for (y = 0; y < height; y++) {
@@ -114,8 +160,7 @@ Bool RSavePNG(RImage *img, const char *filename, char *title)
/* End write */
png_write_end(png_ptr, NULL);
/* Clean */
fclose(file);
/* Clean up structures */
if (png_info_ptr != NULL)
png_free_data(png_ptr, png_info_ptr, PNG_FREE_ALL, -1);
if (png_ptr != NULL)
@@ -123,5 +168,54 @@ Bool RSavePNG(RImage *img, const char *filename, char *title)
if (png_row != NULL)
free(png_row);
/* Return the buffer */
*out_buf = png_data.buffer;
*out_size = png_data.size;
return True;
}
/*
* Save RImage to PNG image
*/
Bool RSavePNG(RImage *img, const char *filename, char *title)
{
FILE *file;
unsigned char *png_data = NULL;
size_t png_size = 0;
size_t written;
if (!img || !filename) {
RErrorCode = RERR_BADIMAGEFILE;
return False;
}
/* Use RSaveRawPNG to generate PNG data in memory */
if (!RSaveRawPNG(img, title, &png_data, &png_size)) {
/* Error code already set by RSaveRawPNG */
return False;
}
/* Open file for writing */
file = fopen(filename, "wb");
if (file == NULL) {
free(png_data);
RErrorCode = RERR_OPEN;
return False;
}
/* Write PNG data to file */
written = fwrite(png_data, 1, png_size, file);
fclose(file);
/* Check if all data was written */
if (written != png_size) {
free(png_data);
RErrorCode = RERR_WRITE;
return False;
}
/* Clean up */
free(png_data);
return True;
}
+24 -3
View File
@@ -244,8 +244,25 @@ static double Mitchell_filter(double t)
return (0.0);
}
static double (*filterf)(double) = Mitchell_filter;
static double fwidth = Mitchell_support;
#define CatmullRom_support (2.0)
static double CatmullRom_filter(double t)
{
double tt;
tt = t * t;
if (t < 0)
t = -t;
if (t < 1.0) {
return ((1.5 * t * tt) - (2.5 * tt) + 1.0);
} else if (t < 2.0) {
return ((-0.5 * t * tt) + (2.5 * tt) - (4.0 * t) + 2.0);
}
return (0.0);
}
static double (*filterf)(double) = CatmullRom_filter;
static double fwidth = CatmullRom_support;
void wraster_change_filter(RScalingFilter type)
{
@@ -270,11 +287,15 @@ void wraster_change_filter(RScalingFilter type)
filterf = Lanczos3_filter;
fwidth = Lanczos3_support;
break;
default:
case RMitchellFilter:
filterf = Mitchell_filter;
fwidth = Mitchell_support;
break;
default:
case RCatmullRomFilter:
filterf = CatmullRom_filter;
fwidth = CatmullRom_support;
break;
}
}
+5 -1
View File
@@ -143,7 +143,8 @@ typedef enum {
RBellFilter,
RBSplineFilter,
RLanczos3Filter,
RMitchellFilter
RMitchellFilter,
RCatmullRomFilter
} RScalingFilter;
@@ -391,6 +392,9 @@ RImage *RGetImageFromXPMData(RContext *context, char **xpmData)
Bool RSaveImage(RImage *image, const char *filename, const char *format)
__wrlib_nonnull(1, 2, 3);
Bool RSaveRawImage(RImage *image, const char *format, unsigned char **out_buf, size_t *out_size)
__wrlib_nonnull(1, 2, 3, 4);
Bool RSaveTitledImage(RImage *image, const char *filename, const char *format, char *title)
__wrlib_nonnull(1, 2, 3);