mirror of
https://github.com/clearlinux/linux-steam-integration.git
synced 2026-09-06 22:01:27 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d0acc9891 | ||
|
|
7bae532d85 | ||
|
|
4f332c9acf | ||
|
|
f2dddd9f88 | ||
|
|
94d5c6d939 | ||
|
|
f4564e2ab8 | ||
|
|
8d85cb3bed | ||
|
|
fb2e84cb97 | ||
|
|
e1f2783d11 | ||
|
|
13afbe69c3 | ||
|
|
b458346f5a | ||
|
|
bf4fcfe533 | ||
|
|
b7ba890433 | ||
|
|
faa0c3a154 | ||
|
|
99e54dd74b | ||
|
|
5d18366a17 | ||
|
|
6154f2373c | ||
|
|
fe80861f66 | ||
|
|
38ec05ce98 | ||
|
|
d983f8ebdf | ||
|
|
4d3abc5e37 | ||
|
|
47e50b789f | ||
|
|
9905a2bc81 | ||
|
|
11e1d8ae5f | ||
|
|
00e78f705e | ||
|
|
54c685bb1a | ||
|
|
a61e22d677 | ||
|
|
93b0f82279 | ||
|
|
d870577a55 | ||
|
|
fd252e265d | ||
|
|
3684dfe11e | ||
|
|
d5b31dbf54 | ||
|
|
f6dcc30d33 | ||
|
|
7a2670f3e1 | ||
|
|
61d9296a6c | ||
|
|
f2bbe963b6 | ||
|
|
4547d8582a | ||
|
|
1db376fa38 | ||
|
|
0bf704fc79 | ||
|
|
cf023ffb6a | ||
|
|
4b36347ade | ||
|
|
d3469e3eb0 | ||
|
|
29081c492d | ||
|
|
ac76372b84 | ||
|
|
6b107f33b5 | ||
|
|
ba8ae8f384 | ||
|
|
d77ad58934 | ||
|
|
2aae934271 | ||
|
|
12d5268252 | ||
|
|
15253b97df | ||
|
|
8dd27f6f24 | ||
|
|
f21559ee78 | ||
|
|
5b17fe9447 | ||
|
|
9ca7ffdd85 | ||
|
|
3b552ec17d | ||
|
|
a3226a293c | ||
|
|
9c40a9d106 | ||
|
|
49d68c52a7 | ||
|
|
d20c230067 | ||
|
|
bd4707f3ca | ||
|
|
1a70578cb1 | ||
|
|
7995688ea2 | ||
|
|
e3360b7ddc | ||
|
|
1700f2616c | ||
|
|
6f8e905569 | ||
|
|
7c0ff5031e | ||
|
|
dd5237e648 | ||
|
|
2282296045 | ||
|
|
ba48264207 | ||
|
|
bddc0f29b1 | ||
|
|
196a039746 | ||
|
|
4ff98e225a | ||
|
|
e49a7873fe | ||
|
|
6659905102 | ||
|
|
16702d0d8f | ||
|
|
00d8831720 | ||
|
|
451ffe7680 |
+2
-18
@@ -1,18 +1,2 @@
|
||||
Makefile
|
||||
Makefile.in
|
||||
aclocal.m4
|
||||
autom4te.cache/
|
||||
build-aux/
|
||||
config.*
|
||||
configure
|
||||
libtool
|
||||
stamp-h1
|
||||
m4/*.m4
|
||||
.deps
|
||||
.dirstamp
|
||||
*.o
|
||||
*.lo
|
||||
*.la
|
||||
.libs
|
||||
/steam
|
||||
/lsi-settings
|
||||
/buildroot/
|
||||
/build/
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "src/libnica"]
|
||||
path = src/libnica
|
||||
url = git@github.com:intel/libnica.git
|
||||
-82
@@ -1,82 +0,0 @@
|
||||
EXTRA_DIST = \
|
||||
LICENSE.LGPL2.1 \
|
||||
README.rst \
|
||||
data/lsi-settings.desktop
|
||||
|
||||
AM_CFLAGS = \
|
||||
-fstack-protector -Wall -pedantic \
|
||||
-Wstrict-prototypes -Wundef -fno-common \
|
||||
-Werror-implicit-function-declaration \
|
||||
-Wformat -Wformat-security -Werror=format-security \
|
||||
-Wno-conversion -Wunused-variable -Wunreachable-code \
|
||||
-Wall -W -D_FORTIFY_SOURCE=2 -std=c99
|
||||
|
||||
AM_CPPFLAGS = \
|
||||
-I $(top_srcdir)/src \
|
||||
-I $(top_srcdir)/src/shim \
|
||||
-I $(top_srcdir)/src/nica \
|
||||
-DSYSTEMCONFDIR=\"$(sysconfdir)\" \
|
||||
-DVENDORDIR=\"$(datadir)/defaults/linux-steam-integration\"
|
||||
|
||||
|
||||
noinst_LTLIBRARIES = \
|
||||
libnica.la \
|
||||
liblsi.la
|
||||
|
||||
bin_PROGRAMS = \
|
||||
steam
|
||||
|
||||
if HAVE_FRONTEND
|
||||
bin_PROGRAMS += lsi-settings
|
||||
|
||||
lsi_settings_SOURCES = \
|
||||
src/frontend/main.c
|
||||
|
||||
lsi_settings_CFLAGS = \
|
||||
$(GTK_CFLAGS) \
|
||||
$(AM_CFLAGS) \
|
||||
-DGDK_VERSION_MAX_ALLOWED=GDK_VERSION_3_4 \
|
||||
-DGDK_VERSION_MIN_REQUIRED=GDK_VERSION_3_4
|
||||
|
||||
lsi_settings_LDADD = \
|
||||
liblsi.la \
|
||||
$(GTK_LIBS)
|
||||
|
||||
desktopdir = $(datadir)/applications
|
||||
desktop_DATA = \
|
||||
data/lsi-settings.desktop
|
||||
|
||||
endif
|
||||
|
||||
# Convenience library to prevent external linking with currently unstable ABI
|
||||
libnica_la_SOURCES = \
|
||||
src/nica/hashmap.h \
|
||||
src/nica/hashmap.c \
|
||||
src/nica/inifile.h \
|
||||
src/nica/inifile.c \
|
||||
src/nica/util.h
|
||||
|
||||
libnica_la_CFLAGS = \
|
||||
$(AM_CFLAGS)
|
||||
|
||||
|
||||
# Convenience library for shared LSI functions
|
||||
liblsi_la_SOURCES = \
|
||||
src/shim/lsi.h \
|
||||
src/shim/lsi.c
|
||||
|
||||
liblsi_la_CFLAGS = \
|
||||
$(AM_CFLAGS)
|
||||
|
||||
liblsi_la_LIBADD = \
|
||||
libnica.la
|
||||
|
||||
steam_SOURCES = \
|
||||
src/shim/shim.c
|
||||
|
||||
steam_LDADD = \
|
||||
libnica.la \
|
||||
liblsi.la
|
||||
|
||||
steam_CFLAGS = \
|
||||
$(AM_CFLAGS)
|
||||
@@ -0,0 +1,161 @@
|
||||
linux-steam-integration
|
||||
-----------------------
|
||||
|
||||
Linux Steam Integration attempts to improve the Steam* gaming experience for users on Linux. In the default mode, it will disable the Steam runtime, and facilitate usage of the distro's own runtime for improved integration and performance. The Steam Runtime is a very old set of Ubuntu libraries, and as such provides a suboptimial experience for many Linux users. A key example of this is broken fontconfig, C++ ABI issues preventing loading, or even the broken theming of GTK widgets on non-Ubuntu distros. These woes have been compounded for users of the open source drivers.
|
||||
|
||||
The LSI tool also allows you to use Steam's runtime if you need to, as currently not all cases are covered. It will also take care to prepare the environment, ensuring that users can load Steam using it's own runtime without hitting any of the issues common to a modern distro, i.e. ABI issues and having to `LD_PRELOAD` libraries to even make Steam start.
|
||||
|
||||
More importantly, LSI achieves all of this by not having to **butcher your existing Steam installation**, as is common in some new tools.
|
||||
|
||||
This project, and by extension Solus, is not officially endorsed by, or affiliated with, Steam*, or its parent company, Valve*.
|
||||
|
||||
|
||||
Linux Steam Integration is a [Solus project](https://solus-project.com/)
|
||||
|
||||

|
||||
|
||||
|
||||
Integrating LSI
|
||||
===============
|
||||
|
||||
To correctly integrate LSI, your Steam package will require modification. LSI must provide the /usr/bin/steam binary, so your Steam package must move the main launcher to a shadow location.
|
||||
|
||||
For users who do not have access to LSI in their distribution, because it has not been integrated yet, please configure with `-Dwith-shim=co-exist` to preserve your existing Steam integrity. Then use the "LSI Steam" shortcut in your menu to launch Steam via LSI.
|
||||
|
||||
**Configuring LSI build**
|
||||
|
||||
There are a number of meson configure options you should be aware of when integrating LSI correctly. The prominent ones are explained here.
|
||||
|
||||
`-Dwith-shim= none | co-exist | replacement`
|
||||
`--disable-replace-steam`
|
||||
|
||||
Control behaviour of the shim build. `none` will disable building the main
|
||||
shim, required for actually launching Steam in the first place.
|
||||
|
||||
`co-exist` will ensure this does not conflict with the `/usr/bin/steam`
|
||||
path, providing a new `lsi-steam` binary and desktop file.
|
||||
|
||||
`replacement` will provide a drop-in replacement `/usr/bin/steam` binary
|
||||
shim responsible for bootstrapping Steam via LSI. This will require you
|
||||
to mask the original steam binary to a new location.
|
||||
|
||||
The default value for this option is::
|
||||
|
||||
replacement
|
||||
|
||||
|
||||
`-Dwith-steam-binary=$PATH`
|
||||
|
||||
Set the absolute path for the shadowed Steam binary.
|
||||
LSI will execv Steam after it has initialised the environment.
|
||||
Note that execv, not execvp, is used, to mask programs that may be
|
||||
in the `$PATH`, hence requiring an absolute path.
|
||||
|
||||
The default value for this option is::
|
||||
|
||||
/usr/lib/steam/steam
|
||||
|
||||
This option is only applicable for distribution integrators, when
|
||||
Steam replacement is enabled.
|
||||
|
||||
|
||||
`-Dwith-preload-libs=$LIBS`
|
||||
|
||||
A colon separated list of libraries that are required to launch Steam
|
||||
when using its own runtime. LSI enables users to switch back to the Steam
|
||||
runtime, and in this instance we manage the LD_PRELOAD environmental variable.
|
||||
Ensure this is correct, and escape $LIB for correct Linux usage.
|
||||
|
||||
The default value for this option is::
|
||||
|
||||
/usr/\$LIB/libX11.so.6:/usr/\$LIB/libstdc++.so.6
|
||||
|
||||
`-Dwith-frontend=$boolean`
|
||||
|
||||
A small UI application is shipped to enable configuration of LSI, which presents
|
||||
a simple GTK3 Dialog to the user. It is not enabled by default, it is up
|
||||
to the integrator to decide if they wish to employ lsi-settings, or implement
|
||||
an alternative.
|
||||
|
||||
The lsi-settings application will only ever write new configurations to the
|
||||
**user** settings file, and requires no special permissions.
|
||||
|
||||
The default value for this option is::
|
||||
|
||||
false
|
||||
|
||||
|
||||
**How LSI Works**
|
||||
|
||||
LSI provides a /usr/bin/steam binary to be used in place of the existing Steam script, which will then correctly set up the environment before swapping the process for the Steam process.
|
||||
When using a full build of LSI, the bootstrap shim will force a correct environment, removing any issues that can cause various older SDL versions to kill Steam. Additionally it will then
|
||||
force `STEAM_RUNTIME=0` if this is set in the config (default behaviour).
|
||||
|
||||
When using the native runtime, and the `intercept` library is enabled, the LSI shim will also set up `LD_AUDIT` to use `liblsi-intercept`. This library will intercept all dynamic linking operations
|
||||
for the main Steam binaries and override their behaviour, allowing Steam to only load a handful of vendored libraries (`libav` fork) as well as its own private libraries. It is then forced to use
|
||||
system libraries for everything else. This means the Steam client, web helper, etc, will use the native SDL, X11, fixing many bugs with video playback, font rendering and such.
|
||||
|
||||
If you are packaging LSI for a distribution, please ensure you provide both a 32-bit and 64-bit build of the intercept library so that the entirety of Steam's library (`.so`) mechanism is tightly
|
||||
controlled by LSI. See the scripts in the root directory of this repository for examples of how to do this.
|
||||
|
||||
Configuration options can be placed in an INI-style configuration file in a series of locations, which are ordered by priority in a cascade::
|
||||
|
||||
~/.config/linux-steam-integration.conf
|
||||
/etc/linux-steam-integration.conf
|
||||
/usr/share/defaults/linux-steam-integration/linux-steam-integration.conf
|
||||
|
||||
The user configuration takes immediate priority. Secondly we have the system-wide configuration for affecting all accounts, and lastly the vendor configuration, which may be provided by the integrator.
|
||||
|
||||
Currently this INI file supports two options. The root section in this INI file must be `[Steam]`.
|
||||
|
||||
`use-native-runtime = $boolean`
|
||||
|
||||
If set to a true boolean value, (yes/true/on), the host OS's native runtime
|
||||
will be used instead of the Steam provided runtime. If this is set to
|
||||
a false boolean value (no/false/off), then the startup will be modified
|
||||
to export the relevant `LD_PRELOAD` and `STEAM_RUNTIME` settings.
|
||||
|
||||
The default value of this variable is true.
|
||||
|
||||
`force-32bit = $boolean`
|
||||
|
||||
If set to a true boolean value (yes/true/on), the shadowed Steam binary will
|
||||
be run via the `linux32` command. This will force the `steam` process
|
||||
and all children to believe they are running on a 32-bit system. This
|
||||
may be useful for 64-bit games that are buggy only on 64-bit.
|
||||
|
||||
If this is set to a false boolean value (no/false/off), then the
|
||||
shadowed Steam binary will be directly executed. Note that on 32-bit
|
||||
OSs this setting is ignored.
|
||||
|
||||
The default value of this variable is false.
|
||||
|
||||
|
||||
Common issues
|
||||
=============
|
||||
|
||||
**Missing tray icon for Steam client using native OS runtime**
|
||||
|
||||
Ensure you have the 32-bit version of libappindicator installed. This is required for the tray icon. libappindicator falls back to a standard X11 tray icon in the absence of desktop appindicator support.
|
||||
|
||||
Related issue: [Steam tray icon missing #2](https://github.com/solus-project/linux-steam-integration/issues/2)
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
`src/shim src/frontend src/lsi src/intercept`:
|
||||
|
||||
Copyright © 2016-2017 Ikey Doherty
|
||||
|
||||
linux-steam-integration is available under the terms of the `LGPL-2.1`
|
||||
|
||||
`data/lsi-steam.desktop`:
|
||||
|
||||
This file borrows translations from the official `steam.desktop` launcher.
|
||||
These are copyright of Valve*.
|
||||
|
||||
See `LICENSE.LGPL2.1 <LICENSE.LGPL2.1>`_ for more details
|
||||
|
||||
|
||||
* Some names may be claimed as the property of others.
|
||||
-148
@@ -1,148 +0,0 @@
|
||||
linux-steam-integration
|
||||
-----------------------
|
||||
|
||||
A helper shim to enable better Steam* integration on Linux systems.
|
||||
This is part of an effort by Solus to enhance Steam for everyone.
|
||||
|
||||
This project, and by extension Solus, is not officially endorsed by, or
|
||||
affiliated with, Steam*, or its parent company, Valve*.
|
||||
|
||||
|
||||
Linux Steam Integration (LSI) is a `Solus Project <https://github.com/solus-project/linux-steam-integration/issues/2>`_
|
||||
|
||||
.. image:: https://build.solus-project.com/logo.png
|
||||
:align: center
|
||||
:alt: LSI is a Solus Project
|
||||
|
||||
|
||||
Integrating LSI
|
||||
===============
|
||||
|
||||
To correctly integrate LSI, your Steam package will require modification.
|
||||
LSI must provide the /usr/bin/steam binary, so your Steam package must
|
||||
move the main launcher to a shadow location.
|
||||
|
||||
**Configuring LSI build**
|
||||
|
||||
There are a number of configure options you should be aware of when integrating
|
||||
LSI correctly. The prominent ones are explained here.
|
||||
|
||||
|
||||
|
||||
``--with-real-steam-binary=$NAME``
|
||||
|
||||
Set the absolute path for the shadowed Steam binary.
|
||||
LSI will execv Steam after it has initialised the environment.
|
||||
Note that execv, not execvp, is used, to mask programs that may be
|
||||
in the ``$PATH``, hence requiring an absolute path.
|
||||
|
||||
The default value for this option is::
|
||||
|
||||
/usr/lib/steam/steam
|
||||
|
||||
``--with-preload-libs=$LIBS``
|
||||
|
||||
A colon separated list of libraries that are required to launch Steam
|
||||
when using its own runtime. LSI enables users to switch back to the Steam
|
||||
runtime, and in this instance we manage the LD_PRELOAD environmental variable.
|
||||
Ensure this is correct, and escape $LIB for correct Linux usage.
|
||||
|
||||
The default value for this option is::
|
||||
|
||||
/usr/\$LIB/libX11.so.6:/usr/\$LIB/libstdc++.so.6
|
||||
|
||||
``--enable-frontend``
|
||||
|
||||
A small UI application is shipped to enable configuration of LSI, which presents
|
||||
a simple GTK3 Dialog to the user. It is not enabled by default, it is up
|
||||
to the integrator to decide if they wish to employ lsi-settings, or implement
|
||||
an alternative.
|
||||
|
||||
The lsi-settings application will only ever write new configurations to the
|
||||
**user** settings file, and requires no special permissions.
|
||||
|
||||
The default value for this option is::
|
||||
|
||||
disabled
|
||||
|
||||
|
||||
**How LSI Works**
|
||||
|
||||
LSI provides a /usr/bin/steam binary to be used in place of the existing Steam script,
|
||||
which will then correctly set up the environment before swapping the process for the
|
||||
Steam process.
|
||||
|
||||
Configuration options can be placed in an INI-style configuration file in a series
|
||||
of locations, which are ordered by priority in a cascade::
|
||||
|
||||
~/.config/linux-steam-integration.conf
|
||||
/etc/linux-steam-integration.conf
|
||||
/usr/share/defaults/linux-steam-integration/linux-steam-integration.conf
|
||||
|
||||
The user configuration takes immediate priority. Secondly we have the system-wide
|
||||
configuration for affecting all accounts, and lastly the vendor configuration,
|
||||
which may be provided by the integrator.
|
||||
|
||||
Currently this INI file supports two options. The root section in this INI file
|
||||
must be ``[Steam]``.
|
||||
|
||||
``use-native-runtime = $boolean``
|
||||
|
||||
If set to a true boolean value, (yes/true/on), the host OS's native runtime
|
||||
will be used instead of the Steam provided runtime. If this is set to
|
||||
a false boolean value (no/false/off), then the startup will be modified
|
||||
to export the relevant ``LD_PRELOAD`` and ``STEAM_RUNTIME`` settings.
|
||||
|
||||
The default value of this variable is ``true``.
|
||||
|
||||
``force-32bit = $boolean``
|
||||
|
||||
If set to a true boolean value (yes/true/on), the shadowed Steam binary will
|
||||
be run via the ``linux32`` command. This will force the ``steam`` process
|
||||
and all children to believe they are running on a 32-bit system. This
|
||||
may be useful for 64-bit games that are buggy only on 64-bit.
|
||||
|
||||
If this is set to a false boolean value (no/false/off), then the
|
||||
shadowed Steam binary will be directly executed. Note that on 32-bit
|
||||
OSs this setting is ignored.
|
||||
|
||||
The default value of this variable is ``false``.
|
||||
|
||||
|
||||
Common issues
|
||||
=============
|
||||
|
||||
**Missing tray icon for Steam client using native OS runtime**
|
||||
|
||||
Ensure you have the 32-bit version of libappindicator installed. This
|
||||
is required for the tray icon. libappindicator falls back to a standard
|
||||
X11 tray icon in the absence of desktop appindicator support.
|
||||
|
||||
Related issue: `Steam tray icon missing #2 <https://github.com/solus-project/linux-steam-integration/issues/2>`_
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
``src/nica``::
|
||||
|
||||
Partial import of libnica, Copyright © Intel Corporation.
|
||||
|
||||
|
||||
Used within the shim component to ensure it is leak free
|
||||
and as lightweight as possible, due to needing to exec the
|
||||
main Steam* launcher.
|
||||
libnica is available under the terms of the `LGPL-2.1` license.
|
||||
|
||||
``src/shim src/frontend``::
|
||||
|
||||
Copyright © 2016 Ikey Doherty
|
||||
|
||||
|
||||
linux-steam-integration is available under the terms of the `LGPL-2.1`
|
||||
|
||||
|
||||
See `LICENSE.LGPL2.1 <LICENSE.LGPL2.1>`_ for more details
|
||||
|
||||
|
||||
* Some names may be claimed as the property of others.
|
||||
@@ -1,20 +0,0 @@
|
||||
LSI is still in its infancy, currently only at a 0.1 release. The time to
|
||||
release was dramatic due to an absolute requirement for non-broken Steam
|
||||
to accompany the Solus 1.2 release
|
||||
|
||||
Going forward, there are more items that need implementing to start shaving
|
||||
more rough edges off of Linux gaming.
|
||||
|
||||
Immediate LSI worklist:
|
||||
|
||||
- [x] Add configurable shim to replace /usr/bin/steam, relying on shadowed launcher
|
||||
- [x] Add super-basic UI to configure said shim
|
||||
- [x] Shove out a 0.1 for Solus 1.2
|
||||
- [ ] Replace existing /usr/bin/steam entirely and decouple the tight runtime logic
|
||||
that is currently enforced.
|
||||
- [ ] Remove requirement for steam.sh $bindir/steam entirely - allowing parallel clients.
|
||||
LSI becomes more self contained and no longer needs to prop up Steam, but replaces
|
||||
much of the support tools.
|
||||
- [ ] Replace the existing Steam "run.sh" style launch facilities
|
||||
- [ ] Get to a point that current ubuntu12_32 runtime is no longer required or
|
||||
wanted.
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
autoreconf --force --install --symlink --warnings=all
|
||||
|
||||
args="\
|
||||
--sysconfdir=/etc \
|
||||
--localstatedir=/var \
|
||||
--prefix=/usr \
|
||||
--enable-silent-rules"
|
||||
|
||||
./configure CFLAGS="-g -O1 $CFLAGS" $args "$@"
|
||||
make clean
|
||||
-103
@@ -1,103 +0,0 @@
|
||||
AC_INIT([linux-steam-integration], 0.2, [ikey@solus-project.com], [linux-steam-integration], [https://github.com/solus-project/linux-steam-integration/issues])
|
||||
AC_CONFIG_AUX_DIR([build-aux])
|
||||
AM_INIT_AUTOMAKE([-Wno-portability no-dist-gzip dist-xz foreign subdir-objects])
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
AC_PROG_CC_STDC
|
||||
LT_PREREQ(2.2)
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_PREFIX_DEFAULT(/usr/local)
|
||||
AM_SILENT_RULES([yes])
|
||||
LT_INIT([disable-static])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
|
||||
# Check if we're using the new C++ ABI, which will require LD_PRELOAD magic
|
||||
AC_LANG_PUSH([C++])
|
||||
AC_MSG_CHECKING(['_GLIBCXX_USE_CXX11_ABI' value])
|
||||
AC_COMPUTE_INT([LSI_NEW_CXX_ABI], [_GLIBCXX_USE_CXX11_ABI], [[#include <string>]], [LSI_NEW_CXX_ABI=0])
|
||||
AC_MSG_RESULT([$LSI_NEW_CXX_ABI])
|
||||
AC_LANG_POP([C++])
|
||||
|
||||
if test x$LSI_NEW_CXX_ABI = "x1"; then
|
||||
use_new_abi="yes"
|
||||
AC_DEFINE([LSI_USE_NEW_ABI], [1], [Using new C++ ABI])
|
||||
else
|
||||
use_new_abi="no"
|
||||
fi
|
||||
|
||||
# Source: https://mail.gnome.org/archives/commits-list/2012-September/msg01730.html
|
||||
# adl_RECURSIVE_EVAL(VALUE, RESULT)
|
||||
# =================================
|
||||
# Interpolate the VALUE in loop until it doesn't change,
|
||||
# and set the result to $RESULT.
|
||||
# WARNING: It's easy to get an infinite loop with some unsane input.
|
||||
# For example ${datadir} becomes ${datarootdir}, and then ${prefix}/share, and
|
||||
# finally ${prefix} is replaced by the prefix.
|
||||
AC_DEFUN([adl_RECURSIVE_EVAL],
|
||||
[_lcl_receval="$1"
|
||||
$2=`(test "x$prefix" = xNONE && prefix="$ac_default_prefix"
|
||||
test "x$exec_prefix" = xNONE && exec_prefix="${prefix}"
|
||||
_lcl_receval_old=''
|
||||
while test "[$]_lcl_receval_old" != "[$]_lcl_receval"; do
|
||||
_lcl_receval_old="[$]_lcl_receval"
|
||||
eval _lcl_receval="\"[$]_lcl_receval\""
|
||||
done
|
||||
echo "[$]_lcl_receval")`])
|
||||
|
||||
# Shadow location of real Steam
|
||||
AC_ARG_WITH([real-steam-binary], AS_HELP_STRING([--with-real-steam-binary],
|
||||
[the real Steam binary to use]), [steambin=${withval}],
|
||||
[steambin="$libdir/steam/steam"])
|
||||
|
||||
adl_RECURSIVE_EVAL(["$steambin"], [STEAMBIN])
|
||||
AC_SUBST(STEAMBIN)
|
||||
|
||||
AC_DEFINE_UNQUOTED(STEAM_BINARY, "$STEAMBIN",
|
||||
[The location of the real Steam binary])
|
||||
|
||||
# The LD_PRELOAD list required to integrate gracefully with the official Steam Runtime
|
||||
AC_ARG_WITH([preload-libs], AS_HELP_STRING([--with-preload-libs],
|
||||
[the preload libs to use with Steams own runtime]), [preloadlibs="${withval}"],
|
||||
[preloadlibs="/usr/\$LIB/libX11.so.6:/usr/\$LIB/libstdc++.so.6"])
|
||||
|
||||
AC_DEFINE_UNQUOTED([LSI_PRELOAD_LIBS], "$preloadlibs",
|
||||
[Libraries to preload with Steams runtime])
|
||||
|
||||
m4_define([gtk_required_version], [3.4.0])
|
||||
|
||||
# Check if frontend is requested
|
||||
have_frontend="no"
|
||||
AC_ARG_ENABLE(frontend, AS_HELP_STRING([--enable-frontend], [enable GTK frontend]))
|
||||
if test "x$enable_frontend" = "xyes" ; then
|
||||
PKG_CHECK_MODULES([GTK], [gtk+-3.0 >= gtk_required_version])
|
||||
AC_DEFINE([HAVE_FRONTEND], [1], [Enabled GTK+ frontend])
|
||||
have_frontend="yes"
|
||||
|
||||
fi
|
||||
AM_CONDITIONAL([HAVE_FRONTEND], [test x$have_frontend = "xyes"])
|
||||
|
||||
|
||||
AC_OUTPUT
|
||||
|
||||
AC_MSG_RESULT([
|
||||
linux-steam-integration $VERSION
|
||||
========
|
||||
|
||||
prefix: ${prefix}
|
||||
libdir: ${libdir}
|
||||
sysconfdir: ${sysconfdir}
|
||||
exec_prefix: ${exec_prefix}
|
||||
bindir: ${bindir}
|
||||
datarootdir: ${datarootdir}
|
||||
|
||||
New C++ ABI: ${use_new_abi}
|
||||
Real Steam binary: ${STEAMBIN}
|
||||
GTK+ Frontend: ${have_frontend}
|
||||
preload-libs: ${preloadlibs}
|
||||
|
||||
compiler: ${CC}
|
||||
cflags: ${CFLAGS}
|
||||
ldflags: ${LDFLAGS}
|
||||
])
|
||||
@@ -0,0 +1,107 @@
|
||||
[Desktop Entry]
|
||||
Name=LSI Steam
|
||||
Comment=Launch Steam via LSI
|
||||
Exec=/usr/bin/lsi-steam %U
|
||||
Icon=steam
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Network;FileTransfer;Game;
|
||||
MimeType=x-scheme-handler/steam;
|
||||
Actions=Store;Community;Library;Servers;Screenshots;News;Settings;BigPicture;Friends;
|
||||
|
||||
[Desktop Action Store]
|
||||
Name=Store
|
||||
Name[de]=Shop
|
||||
Name[es]=Tienda
|
||||
Name[fr]=Magasin
|
||||
Name[it]=Negozio
|
||||
Name[pt]=Loja
|
||||
Name[ru]=Магазин
|
||||
Name[zh_CN]=商店
|
||||
Name[zh_TW]=商店
|
||||
Exec=lsi-steam steam://store
|
||||
|
||||
[Desktop Action Community]
|
||||
Name=Community
|
||||
Name[es]=Comunidad
|
||||
Name[fr]=Communauté
|
||||
Name[it]=Comunità
|
||||
Name[pt]=Comunidade
|
||||
Name[ru]=Сообщество
|
||||
Name[zh_CN]=社区
|
||||
Name[zh_TW]=社群
|
||||
Exec=lsi-steam steam://url/SteamIDControlPage
|
||||
|
||||
[Desktop Action Library]
|
||||
Name=Library
|
||||
Name[de]=Bibliothek
|
||||
Name[es]=Biblioteca
|
||||
Name[fr]=Bibliothèque
|
||||
Name[it]=Libreria
|
||||
Name[pt]=Biblioteca
|
||||
Name[ru]=Библиотека
|
||||
Name[zh_CN]=库
|
||||
Name[zh_TW]=遊戲庫
|
||||
Exec=lsi-steam steam://open/games
|
||||
|
||||
[Desktop Action Servers]
|
||||
Name=Servers
|
||||
Name[de]=Server
|
||||
Name[es]=Servidores
|
||||
Name[fr]=Serveurs
|
||||
Name[it]=Server
|
||||
Name[pt]=Servidores
|
||||
Name[ru]=Серверы
|
||||
Name[zh_CN]=服务器
|
||||
Name[zh_TW]=伺服器
|
||||
Exec=lsi-steam steam://open/servers
|
||||
|
||||
[Desktop Action Screenshots]
|
||||
Name=Screenshots
|
||||
Name[es]=Capturas
|
||||
Name[fr]=Captures d'écran
|
||||
Name[it]=Screenshot
|
||||
Name[ru]=Скриншоты
|
||||
Name[zh_CN]=截图
|
||||
Name[zh_TW]=螢幕擷圖
|
||||
Exec=lsi-steam steam://open/screenshots
|
||||
|
||||
[Desktop Action News]
|
||||
Name=News
|
||||
Name[de]=Neuigkeiten
|
||||
Name[es]=Noticias
|
||||
Name[fr]=Actualités
|
||||
Name[it]=Notizie
|
||||
Name[pt]=Notícias
|
||||
Name[ru]=Новости
|
||||
Name[zh_CN]=新闻
|
||||
Name[zh_TW]=新聞
|
||||
Exec=lsi-steam steam://open/news
|
||||
|
||||
[Desktop Action Settings]
|
||||
Name=Settings
|
||||
Name[de]=Einstellungen
|
||||
Name[es]=Parámetros
|
||||
Name[fr]=Paramètres
|
||||
Name[it]=Impostazioni
|
||||
Name[pt]=Configurações
|
||||
Name[ru]=Настройки
|
||||
Name[zh_CN]=设置
|
||||
Name[zh_TW]=設定
|
||||
Exec=lsi-steam steam://open/settings
|
||||
|
||||
[Desktop Action BigPicture]
|
||||
Name=Big Picture
|
||||
Exec=lsi-steam steam://open/bigpicture
|
||||
|
||||
[Desktop Action Friends]
|
||||
Name=Friends
|
||||
Name[de]=Freunde
|
||||
Name[es]=Amigos
|
||||
Name[fr]=Amis
|
||||
Name[it]=Amici
|
||||
Name[pt]=Amigos
|
||||
Name[ru]=Друзья
|
||||
Name[zh_CN]=好友
|
||||
Name[zh_TW]=好友
|
||||
Exec=lsi-steam steam://open/friends
|
||||
@@ -0,0 +1,17 @@
|
||||
appsdir = join_paths(datadir, 'applications')
|
||||
|
||||
# If frontend is enabled, install lsi-settings.desktop
|
||||
if with_frontend == true
|
||||
install_data(
|
||||
'lsi-settings.desktop',
|
||||
install_dir: appsdir,
|
||||
)
|
||||
endif
|
||||
|
||||
# If we need to co-exist, install alternative .desktop file (not conflicting with Steam)
|
||||
if shim_behaviour == 'co-exist'
|
||||
install_data(
|
||||
'lsi-steam.desktop',
|
||||
install_dir: appsdir,
|
||||
)
|
||||
endif
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
project(
|
||||
'linux-steam-integration',
|
||||
['c'],
|
||||
version: '0.4',
|
||||
license: [
|
||||
'LGPL-2.1',
|
||||
],
|
||||
default_options: [
|
||||
'c_std=c99',
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
am_cflags = [
|
||||
'-fstack-protector',
|
||||
'-pedantic',
|
||||
'-Wstrict-prototypes',
|
||||
'-Wundef',
|
||||
'-fno-common',
|
||||
'-Werror-implicit-function-declaration',
|
||||
'-Wformat',
|
||||
'-Wformat-security',
|
||||
'-Werror=format-security',
|
||||
'-Wno-conversion',
|
||||
'-Wunused-variable',
|
||||
'-Wunreachable-code',
|
||||
'-Wall',
|
||||
'-W',
|
||||
'-D_FORTIFY_SOURCE=2',
|
||||
]
|
||||
|
||||
add_global_arguments(am_cflags, language: 'c')
|
||||
|
||||
dep_gtk3 = dependency('gtk+-3.0', version: '>= 3.4.0')
|
||||
|
||||
cdata = configuration_data()
|
||||
|
||||
cdata.set_quoted('PACKAGE_NAME', meson.project_name())
|
||||
|
||||
prefix = get_option('prefix')
|
||||
datadir = join_paths(prefix, get_option('datadir'))
|
||||
vendordir = join_paths(datadir, 'defaults', meson.project_name())
|
||||
sysconfdir = join_paths(prefix, get_option('sysconfdir'))
|
||||
cdata.set_quoted('SYSTEMCONFDIR', sysconfdir)
|
||||
cdata.set_quoted('VENDORDIR', vendordir)
|
||||
|
||||
# Configurables
|
||||
cdata.set_quoted('LSI_PRELOAD_LIBS', get_option('with-preload-libs'))
|
||||
cdata.set_quoted('STEAM_BINARY', get_option('with-steam-binary'))
|
||||
|
||||
shim_behaviour = get_option('with-shim')
|
||||
if shim_behaviour == 'replacement'
|
||||
cdata.set('REPLACE_STEAM', '1')
|
||||
endif
|
||||
|
||||
with_libintercept = get_option('with-libintercept')
|
||||
if with_libintercept == true
|
||||
cdata.set('HAVE_LIBINTERCEPT', '1')
|
||||
endif
|
||||
|
||||
with_frontend = get_option('with-frontend')
|
||||
|
||||
config_h = configure_file(
|
||||
configuration: cdata,
|
||||
output: 'config.h',
|
||||
)
|
||||
config_h_dir = include_directories('.')
|
||||
|
||||
subdir('data')
|
||||
subdir('src')
|
||||
@@ -0,0 +1,7 @@
|
||||
option('with-shim', type : 'combo', choices : ['replacement', 'co-exist', 'none'], default: 'replacement',
|
||||
description : 'control how the shim is deployed')
|
||||
option('with-steam-binary', type: 'string', description: 'Set the path to the Steam binary', value: '/usr/lib/steam/steam')
|
||||
option('with-frontend', type: 'boolean', description: 'Build the LSI Settings UI', value: false)
|
||||
option('with-preload-libs', type: 'string', description: 'Colon separated list of libraries required to launch Steam games',
|
||||
value: '/usr/\$LIB/libxcb.so.1:/usr/\$LIB/libX11.so.6:/usr/\$LIB/libstdc++.so.6')
|
||||
option('with-libintercept', type: 'boolean', description: 'Enable libintercept library for controlling Steam linking', value: true)
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
if [[ ! -d buildroot/build32 ]]; then
|
||||
CC="gcc -m32" linux32 meson buildroot/build32 --prefix=/usr --sysconfdir=/etc --datadir=/usr/share --libdir=/usr/lib32 -Dwith-shim=none
|
||||
fi
|
||||
|
||||
if [[ ! -d buildroot/build64 ]]; then
|
||||
meson buildroot/build64 --prefix=/usr --sysconfdir=/etc --datadir=/usr/share --libdir=/usr/lib64 -Dwith-shim=replacement -Dwith-steam-binary=/usr/lib64/steam/steam -Dwith-frontend=true
|
||||
fi
|
||||
|
||||
ninja -C buildroot/build32
|
||||
ninja -C buildroot/build64
|
||||
|
||||
sudo ninja -C buildroot/build32 install
|
||||
sudo ninja -C buildroot/build64 install
|
||||
|
||||
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
git submodule init
|
||||
git submodule update
|
||||
|
||||
# Script for ikey because he went with meson. *shrug*
|
||||
VERSION="0.4"
|
||||
NAME="linux-steam-integration"
|
||||
git-archive-all.sh --format tar --prefix ${NAME}-${VERSION}/ --verbose -t HEAD ${NAME}-${VERSION}.tar
|
||||
xz -9 "${NAME}-${VERSION}.tar"
|
||||
|
||||
gpg --armor --detach-sign "${NAME}-${VERSION}.tar.xz"
|
||||
gpg --verify "${NAME}-${VERSION}.tar.xz.asc"
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* This file is part of linux-steam-integration.
|
||||
*
|
||||
* Copyright © 2016-2017 Ikey Doherty
|
||||
*
|
||||
* linux-steam-integration is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1
|
||||
* of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
/* Mark a variable/argument as unused to skip warnings */
|
||||
#define __lsi_unused__ __attribute__((unused))
|
||||
|
||||
/* Force inlining of a function */
|
||||
#define __lsi_inline__ __attribute__((always_inline))
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
|
||||
|
||||
/*
|
||||
* Editor modelines - https://www.wireshark.org/tools/modelines.html
|
||||
*
|
||||
* Local variables:
|
||||
* c-basic-offset: 8
|
||||
* tab-width: 8
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*
|
||||
* vi: set shiftwidth=8 tabstop=8 expandtab:
|
||||
* :indentSize=8:tabSize=8:noTabs=true:
|
||||
*/
|
||||
+23
-1
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of linux-steam-integration.
|
||||
*
|
||||
* Copyright © 2016 Ikey Doherty
|
||||
* Copyright © 2016-2017 Ikey Doherty <ikey@solus-project.com>
|
||||
*
|
||||
* linux-steam-integration is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License as
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "lsi.h"
|
||||
|
||||
static inline void _align_label(GtkWidget *label)
|
||||
@@ -40,6 +41,9 @@ int main(int argc, char **argv)
|
||||
GtkWidget *image = NULL;
|
||||
GtkWidget *check_native = NULL;
|
||||
GtkWidget *check_emul32 = NULL;
|
||||
#ifdef HAVE_LIBINTERCEPT
|
||||
GtkWidget *check_intercept = NULL;
|
||||
#endif
|
||||
GtkWidget *label = NULL;
|
||||
GtkSettings *settings = NULL;
|
||||
int response = 0;
|
||||
@@ -110,6 +114,17 @@ int main(int argc, char **argv)
|
||||
|
||||
gtk_widget_set_sensitive(label, is_x86_64);
|
||||
gtk_widget_set_sensitive(check_emul32, is_x86_64);
|
||||
|
||||
#ifdef HAVE_LIBINTERCEPT
|
||||
label = gtk_label_new("Use liblsi-intercept to override Steam's library loading mechanism");
|
||||
_align_label(label);
|
||||
gtk_widget_set_tooltip_text(label,
|
||||
"This can help with many library issues with the core client");
|
||||
gtk_grid_attach(GTK_GRID(grid), label, 1, 3, 1, 1);
|
||||
check_intercept = gtk_switch_new();
|
||||
gtk_grid_attach(GTK_GRID(grid), check_intercept, 2, 3, 1, 1);
|
||||
#endif
|
||||
|
||||
gtk_container_set_border_width(GTK_CONTAINER(grid), 6);
|
||||
gtk_widget_set_margin_bottom(grid, 18);
|
||||
|
||||
@@ -122,6 +137,10 @@ int main(int argc, char **argv)
|
||||
gtk_switch_set_active(GTK_SWITCH(check_native), lconfig.use_native_runtime);
|
||||
gtk_switch_set_active(GTK_SWITCH(check_emul32), lconfig.force_32);
|
||||
|
||||
#ifdef HAVE_LIBINTERCEPT
|
||||
gtk_switch_set_active(GTK_SWITCH(check_intercept), lconfig.use_libintercept);
|
||||
#endif
|
||||
|
||||
/* Run the dialog */
|
||||
gtk_widget_show_all(container);
|
||||
response = gtk_dialog_run(GTK_DIALOG(dialog));
|
||||
@@ -134,6 +153,9 @@ int main(int argc, char **argv)
|
||||
/* Get the config from the UI */
|
||||
lconfig.use_native_runtime = gtk_switch_get_active(GTK_SWITCH(check_native));
|
||||
lconfig.force_32 = gtk_switch_get_active(GTK_SWITCH(check_emul32));
|
||||
#ifdef HAVE_LIBINTERCEPT
|
||||
lconfig.use_libintercept = gtk_switch_get_active(GTK_SWITCH(check_intercept));
|
||||
#endif
|
||||
|
||||
/* Cleanup */
|
||||
gtk_widget_destroy(dialog);
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
if with_frontend == true
|
||||
dep_gtk3 = dependency('gtk+-3.0', version: '>= 3.4.0')
|
||||
|
||||
frontend_sources = [
|
||||
'main.c',
|
||||
]
|
||||
|
||||
frontend = executable(
|
||||
'lsi-settings',
|
||||
sources: frontend_sources + nica_sources,
|
||||
include_directories: nica_includes,
|
||||
dependencies: [
|
||||
dep_gtk3,
|
||||
link_lsi,
|
||||
],
|
||||
install: true,
|
||||
)
|
||||
endif
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* This file is part of linux-steam-integration.
|
||||
*
|
||||
* Copyright © 2016-2017 Ikey Doherty <ikey@solus-project.com>
|
||||
*
|
||||
* linux-steam-integration is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1
|
||||
* of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <link.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../common.h"
|
||||
#include "nica/nica.h"
|
||||
|
||||
/**
|
||||
* Is this a process we're actually interested in?
|
||||
*/
|
||||
static bool process_supported = false;
|
||||
static const char *matched_process = NULL;
|
||||
|
||||
/**
|
||||
* Determine the basename'd process
|
||||
*/
|
||||
static inline char *get_process_name(void)
|
||||
{
|
||||
autofree(char) *realp = NULL;
|
||||
char *basep = NULL;
|
||||
|
||||
realp = realpath("/proc/self/exe", NULL);
|
||||
if (!realp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
basep = basename(realp);
|
||||
return strdup(basep);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find out if we're being executed by a process we actually need to override,
|
||||
* otherwise we'd not be loaded by rtld-audit
|
||||
*/
|
||||
static bool is_intercept_candidate(void)
|
||||
{
|
||||
static const char *wanted_processes[] = {
|
||||
"html5app_steam",
|
||||
"opengl-program",
|
||||
"steam",
|
||||
"steamwebhelper",
|
||||
};
|
||||
autofree(char) *nom = NULL;
|
||||
|
||||
nom = get_process_name();
|
||||
if (!nom) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(wanted_processes); i++) {
|
||||
if (streq(wanted_processes[i], nom)) {
|
||||
matched_process = wanted_processes[i];
|
||||
if (!getenv("LSI_DEBUG")) {
|
||||
return true;
|
||||
}
|
||||
fprintf(stderr,
|
||||
"\033[32;1m[LSI:%s]\033[0m: debug: loading libintercept from LSI\n",
|
||||
matched_process);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* la_version is our main entry point and will only continue if our
|
||||
* process is explicitly Steam
|
||||
*/
|
||||
_nica_public_ unsigned int la_version(unsigned int supported_version)
|
||||
{
|
||||
/* Unfortunately glibc will die if we tell it to skip us .. */
|
||||
process_supported = is_intercept_candidate();
|
||||
return supported_version;
|
||||
}
|
||||
|
||||
/**
|
||||
* emit_overriden lib is used to pretty-print the debug when we blacklist
|
||||
* a vendored library
|
||||
*/
|
||||
static void emit_overriden_lib(const char *lib_name)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"\033[32;1m[LSI:%s]\033[0m: debug: blacklisted loading of vendored library: "
|
||||
"\033[34;1m%s\033[0m\n",
|
||||
matched_process,
|
||||
lib_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* la_objsearch will allow us to blacklist certain LD_LIBRARY_PATH duplicate
|
||||
* libraries being loaded by the Steam client, such as the broken libSDL shipped
|
||||
* as a private vendored lib
|
||||
*/
|
||||
_nica_public_ char *la_objsearch(const char *name, __lsi_unused__ uintptr_t *cookie,
|
||||
__lsi_unused__ unsigned int flag)
|
||||
{
|
||||
/* We don't know about this process, so have glibc do its thing as normal */
|
||||
if (!process_supported) {
|
||||
return (char *)name;
|
||||
}
|
||||
|
||||
// Patterns we'll permit Steam to privately load
|
||||
static const char *steam_allowed[] = {
|
||||
/* general */
|
||||
"libicui18n.so",
|
||||
"libicuuc.so",
|
||||
"libavcodec.so.",
|
||||
"libavformat.so.",
|
||||
"libavresample.so.",
|
||||
"libavutil.so.",
|
||||
"libswscale.so.",
|
||||
|
||||
/* core plugins */
|
||||
"chromehtml.so",
|
||||
"crashhandler.so",
|
||||
"filesystem_stdio.so",
|
||||
"friendsui.so",
|
||||
"gameoverlayrenderer.so",
|
||||
"gameoverlayui.so",
|
||||
"libaudio.so",
|
||||
"libicui18n.so",
|
||||
"libicuuc.so",
|
||||
"libmiles.so",
|
||||
"libopenvr_api.so",
|
||||
"liboverride.so",
|
||||
"libsteam.so",
|
||||
"libtier0_s.so",
|
||||
"libv8.so",
|
||||
"libvideo.so",
|
||||
"libvstdlib_s.so",
|
||||
"serverbrowser.so",
|
||||
"steamclient.so",
|
||||
"steamoverlayvulkanlayer.so",
|
||||
"steamservice.so",
|
||||
"steamui.so",
|
||||
"vgui2_s.so",
|
||||
|
||||
/* big picture mode */
|
||||
"panorama",
|
||||
"libpangoft2-1.0.so",
|
||||
"libpango-1.0.so",
|
||||
|
||||
/* steamwebhelper */
|
||||
"libcef.so",
|
||||
};
|
||||
|
||||
/* Find out if it exists */
|
||||
bool file_exists = nc_file_exists(name);
|
||||
|
||||
/* Find out if its a Steam private lib.. These are relative "./" files too! */
|
||||
if (name && (strstr(name, "/Steam/") || strncmp(name, "./", 2) == 0)) {
|
||||
for (size_t i = 0; i < ARRAY_SIZE(steam_allowed); i++) {
|
||||
if (strstr(name, steam_allowed[i])) {
|
||||
return (char *)name;
|
||||
}
|
||||
}
|
||||
/* If LSI_DEBUG is set, spam it. */
|
||||
if (getenv("LSI_DEBUG") && file_exists) {
|
||||
emit_overriden_lib(name);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (char *)name;
|
||||
}
|
||||
|
||||
/*
|
||||
* Editor modelines - https://www.wireshark.org/tools/modelines.html
|
||||
*
|
||||
* Local variables:
|
||||
* c-basic-offset: 8
|
||||
* tab-width: 8
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*
|
||||
* vi: set shiftwidth=8 tabstop=8 expandtab:
|
||||
* :indentSize=8:tabSize=8:noTabs=true:
|
||||
*/
|
||||
@@ -0,0 +1,15 @@
|
||||
# Only build libintercept if told to!
|
||||
|
||||
if with_libintercept == true
|
||||
|
||||
intercept_sources = [
|
||||
'main.c',
|
||||
]
|
||||
|
||||
main_intercept = shared_library(
|
||||
'lsi-intercept',
|
||||
sources: intercept_sources + nica_sources,
|
||||
include_directories: nica_includes,
|
||||
install: true,
|
||||
)
|
||||
endif
|
||||
Submodule
+1
Submodule src/libnica added at e71a442667
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of linux-steam-integration.
|
||||
*
|
||||
* Copyright © 2016 Ikey Doherty
|
||||
* Copyright © 2016-2017 Ikey Doherty <ikey@solus-project.com>
|
||||
*
|
||||
* linux-steam-integration is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License as
|
||||
@@ -143,6 +143,13 @@ bool lsi_config_load(LsiConfig *config)
|
||||
map_val = NULL;
|
||||
}
|
||||
|
||||
/* Do we want libintercept? */
|
||||
map_val = nc_hashmap_get(nc_hashmap_get(mconfig, "Steam"), "use-libintercept");
|
||||
if (map_val) {
|
||||
config->use_libintercept = lsi_is_boolean_true(map_val);
|
||||
map_val = NULL;
|
||||
}
|
||||
|
||||
/* Check if 32-bit is being forced */
|
||||
map_val = nc_hashmap_get(nc_hashmap_get(mconfig, "Steam"), "force-32bit");
|
||||
if (map_val) {
|
||||
@@ -178,9 +185,10 @@ bool lsi_config_store(LsiConfig *config)
|
||||
return false;
|
||||
}
|
||||
if (fprintf(fp,
|
||||
"[Steam]\nuse-native-runtime = %s\nforce-32bit = %s\n",
|
||||
"[Steam]\nuse-native-runtime = %s\nforce-32bit = %s\nuse-libintercept = %s\n",
|
||||
lsi_bool_to_string(config->use_native_runtime),
|
||||
lsi_bool_to_string(config->force_32)) < 0) {
|
||||
lsi_bool_to_string(config->force_32),
|
||||
lsi_bool_to_string(config->use_libintercept)) < 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -194,6 +202,7 @@ void lsi_config_load_defaults(LsiConfig *config)
|
||||
* things that LSI knows about */
|
||||
config->force_32 = false;
|
||||
config->use_native_runtime = true;
|
||||
config->use_libintercept = true;
|
||||
}
|
||||
|
||||
void lsi_report_failure(const char *s, ...)
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of linux-steam-integration.
|
||||
*
|
||||
* Copyright © 2016 Ikey Doherty
|
||||
* Copyright © 2016-2017 Ikey Doherty <ikey@solus-project.com>
|
||||
*
|
||||
* linux-steam-integration is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License as
|
||||
@@ -16,25 +16,20 @@
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../common.h"
|
||||
#include "config.h"
|
||||
|
||||
/* Mark a variable/argument as unused to skip warnings */
|
||||
#define __lsi_unused__ __attribute__((unused))
|
||||
|
||||
/* Force inlining of a function */
|
||||
#define __lsi_inline__ __attribute__((always_inline))
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
|
||||
|
||||
/**
|
||||
* Current Linux Steam Integration settings.
|
||||
*/
|
||||
typedef struct LsiConfig {
|
||||
bool force_32; /**<Do we force 32-bit? */
|
||||
bool use_native_runtime; /**<Do we force our native runtime? */
|
||||
bool use_libintercept; /**<Do we force libintercept? */
|
||||
} LsiConfig;
|
||||
|
||||
/**
|
||||
@@ -0,0 +1,18 @@
|
||||
# Only build liblsi if told to!
|
||||
|
||||
if shim_behaviour != 'none' or with_frontend == true
|
||||
|
||||
lsi_sources = [
|
||||
'lsi.c',
|
||||
]
|
||||
|
||||
lib_lsi = static_library(
|
||||
'lsi-lsi',
|
||||
sources: lsi_sources + nica_sources,
|
||||
include_directories: nica_includes,
|
||||
)
|
||||
link_lsi = declare_dependency(
|
||||
link_with: lib_lsi,
|
||||
include_directories: include_directories('.'),
|
||||
)
|
||||
endif
|
||||
@@ -0,0 +1,22 @@
|
||||
# Track how to build nica, as we actually build it twice..
|
||||
|
||||
nica_sources = [
|
||||
'../libnica/src/array.c',
|
||||
'../libnica/src/hashmap.c',
|
||||
'../libnica/src/files.c',
|
||||
'../libnica/src/inifile.c',
|
||||
'../libnica/src/list.c',
|
||||
'../libnica/src/nc-string.c',
|
||||
'../libnica/src/util.c',
|
||||
]
|
||||
|
||||
nica_includes = [
|
||||
include_directories('libnica/src/include'),
|
||||
include_directories('libnica/src/include/nica'),
|
||||
config_h_dir,
|
||||
]
|
||||
|
||||
subdir('lsi')
|
||||
subdir('frontend')
|
||||
subdir('intercept')
|
||||
subdir('shim')
|
||||
@@ -1,472 +0,0 @@
|
||||
/*
|
||||
* This file is part of libnica.
|
||||
*
|
||||
* Copyright (C) 2016 Intel Corporation
|
||||
*
|
||||
* libnica is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1
|
||||
* of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hashmap.h"
|
||||
|
||||
#define INITIAL_SIZE 61
|
||||
|
||||
/* When we're "full", 70% */
|
||||
#define FULL_FACTOR 0.7
|
||||
|
||||
/* Multiple to increase bucket size by */
|
||||
#define INCREASE_FACTOR 4
|
||||
|
||||
/**
|
||||
* An bucket/chain within the hashmap
|
||||
*/
|
||||
typedef struct NcHashmapEntry {
|
||||
void *hash; /**<The key for this item */
|
||||
void *value; /**<Value for this item */
|
||||
struct NcHashmapEntry *next; /**<Next item in the chain */
|
||||
bool occ; /**<Whether this bucket is occupied */
|
||||
} NcHashmapEntry;
|
||||
|
||||
/**
|
||||
* A NcHashmap
|
||||
*/
|
||||
struct NcHashmap {
|
||||
int size; /**<Current size of the hashmap */
|
||||
int next_size; /**<Next size at which we need to resize */
|
||||
int n_buckets; /**<Current number of buckets */
|
||||
NcHashmapEntry *buckets; /**<Stores our bucket chains */
|
||||
|
||||
nc_hash_create_func hash; /**<Hash generation function */
|
||||
nc_hash_compare_func compare; /**<Key comparison function */
|
||||
nc_hash_free_func key_free; /**<Cleanup function for keys */
|
||||
nc_hash_free_func value_free; /**<Cleanup function for values */
|
||||
};
|
||||
|
||||
/**
|
||||
* Iteration object
|
||||
*/
|
||||
typedef struct _NcHashmapIter {
|
||||
int bucket; /**<Current bucket position */
|
||||
NcHashmap *map; /**<Associated NcHashmap */
|
||||
void *item; /**<Current item in this iteration */
|
||||
} _NcHashmapIter;
|
||||
|
||||
static void nc_hashmap_update_next_size(NcHashmap *self);
|
||||
static bool nc_hashmap_resize(NcHashmap *self);
|
||||
|
||||
static inline bool nc_hashmap_maybe_resize(NcHashmap *self)
|
||||
{
|
||||
if (!self) {
|
||||
return false;
|
||||
}
|
||||
if (self->size >= self->next_size) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static NcHashmap *nc_hashmap_new_internal(nc_hash_create_func create, nc_hash_compare_func compare,
|
||||
nc_hash_free_func key_free, nc_hash_free_func value_free)
|
||||
{
|
||||
NcHashmap *map = NULL;
|
||||
NcHashmapEntry *buckets = NULL;
|
||||
|
||||
map = calloc(1, sizeof(NcHashmap));
|
||||
if (!map) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buckets = calloc(INITIAL_SIZE, sizeof(NcHashmapEntry));
|
||||
if (!buckets) {
|
||||
free(map);
|
||||
return NULL;
|
||||
}
|
||||
map->buckets = buckets;
|
||||
map->n_buckets = INITIAL_SIZE;
|
||||
map->hash = create ? create : nc_simple_hash;
|
||||
map->compare = compare ? compare : nc_simple_compare;
|
||||
map->key_free = key_free;
|
||||
map->value_free = value_free;
|
||||
map->size = 0;
|
||||
|
||||
nc_hashmap_update_next_size(map);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
NcHashmap *nc_hashmap_new(nc_hash_create_func create, nc_hash_compare_func compare)
|
||||
{
|
||||
return nc_hashmap_new_internal(create, compare, NULL, NULL);
|
||||
}
|
||||
|
||||
NcHashmap *nc_hashmap_new_full(nc_hash_create_func create, nc_hash_compare_func compare,
|
||||
nc_hash_free_func key_free, nc_hash_free_func value_free)
|
||||
{
|
||||
return nc_hashmap_new_internal(create, compare, key_free, value_free);
|
||||
}
|
||||
|
||||
static inline unsigned nc_hashmap_get_hash(NcHashmap *self, const void *key)
|
||||
{
|
||||
unsigned hash = self->hash(key);
|
||||
return hash;
|
||||
}
|
||||
|
||||
static bool nc_hashmap_insert_bucket(NcHashmap *self, NcHashmapEntry *buckets, int n_buckets,
|
||||
unsigned hash, const void *key, void *value)
|
||||
{
|
||||
NcHashmapEntry *row = &(buckets[hash % n_buckets]);
|
||||
NcHashmapEntry *head = NULL;
|
||||
NcHashmapEntry *parent = head = row;
|
||||
bool can_replace = false;
|
||||
NcHashmapEntry *tomb = NULL;
|
||||
int ret = 1;
|
||||
|
||||
while (row) {
|
||||
if (!row->occ) {
|
||||
tomb = row;
|
||||
}
|
||||
parent = row;
|
||||
if (row->occ && row->hash == key) {
|
||||
if (self->compare(row->hash, key)) {
|
||||
can_replace = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
row = row->next;
|
||||
}
|
||||
|
||||
if (can_replace) {
|
||||
/* Replace existing allocations. */
|
||||
if (self->value_free) {
|
||||
self->value_free(row->value);
|
||||
}
|
||||
if (self->key_free) {
|
||||
self->key_free(row->hash);
|
||||
}
|
||||
ret = 0;
|
||||
} else if (tomb) {
|
||||
row = tomb;
|
||||
}
|
||||
|
||||
if (!row) {
|
||||
row = calloc(1, sizeof(NcHashmapEntry));
|
||||
if (!row) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
row->hash = (void *)key;
|
||||
row->value = value;
|
||||
row->occ = true;
|
||||
if (parent != row && parent) {
|
||||
parent->next = row;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool nc_hashmap_put(NcHashmap *self, const void *key, void *value)
|
||||
{
|
||||
if (!self) {
|
||||
return false;
|
||||
}
|
||||
int inc;
|
||||
|
||||
if (nc_hashmap_maybe_resize(self)) {
|
||||
if (!nc_hashmap_resize(self)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
unsigned hash = nc_hashmap_get_hash(self, key);
|
||||
inc = nc_hashmap_insert_bucket(self, self->buckets, self->n_buckets, hash, key, value);
|
||||
if (inc > 0) {
|
||||
self->size += inc;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static NcHashmapEntry *nc_hashmap_get_entry(NcHashmap *self, const void *key)
|
||||
{
|
||||
if (!self) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned hash = nc_hashmap_get_hash(self, key);
|
||||
NcHashmapEntry *row = &(self->buckets[hash % self->n_buckets]);
|
||||
|
||||
while (row) {
|
||||
if (self->compare(row->hash, key)) {
|
||||
return row;
|
||||
}
|
||||
row = row->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *nc_hashmap_get(NcHashmap *self, const void *key)
|
||||
{
|
||||
if (!self) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NcHashmapEntry *row = nc_hashmap_get_entry(self, key);
|
||||
if (row) {
|
||||
return row->value;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool nc_hashmap_remove_internal(NcHashmap *self, const void *key, bool remove)
|
||||
{
|
||||
if (!self) {
|
||||
return false;
|
||||
}
|
||||
NcHashmapEntry *row = nc_hashmap_get_entry(self, key);
|
||||
|
||||
if (!row) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (remove) {
|
||||
if (self->key_free) {
|
||||
self->key_free(row->hash);
|
||||
}
|
||||
if (self->value_free) {
|
||||
self->value_free(row->value);
|
||||
}
|
||||
}
|
||||
self->size -= 1;
|
||||
row->hash = NULL;
|
||||
row->value = NULL;
|
||||
row->occ = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool nc_hashmap_steal(NcHashmap *self, const void *key)
|
||||
{
|
||||
return nc_hashmap_remove_internal(self, key, false);
|
||||
}
|
||||
|
||||
bool nc_hashmap_remove(NcHashmap *self, const void *key)
|
||||
{
|
||||
return nc_hashmap_remove_internal(self, key, true);
|
||||
}
|
||||
|
||||
bool nc_hashmap_contains(NcHashmap *self, const void *key)
|
||||
{
|
||||
return (nc_hashmap_get(self, key)) != NULL;
|
||||
}
|
||||
|
||||
static inline void nc_hashmap_free_bucket(NcHashmap *self, NcHashmapEntry *bucket, bool nuke)
|
||||
{
|
||||
if (!self) {
|
||||
return;
|
||||
}
|
||||
NcHashmapEntry *tmp = bucket;
|
||||
NcHashmapEntry *bk = bucket;
|
||||
NcHashmapEntry *root = bucket;
|
||||
|
||||
while (tmp) {
|
||||
bk = NULL;
|
||||
if (tmp->next) {
|
||||
bk = tmp->next;
|
||||
}
|
||||
|
||||
if (nuke && tmp->occ) {
|
||||
if (self->key_free) {
|
||||
self->key_free(tmp->hash);
|
||||
}
|
||||
if (self->value_free) {
|
||||
self->value_free(tmp->value);
|
||||
}
|
||||
}
|
||||
if (tmp != root) {
|
||||
free(tmp);
|
||||
}
|
||||
tmp = bk;
|
||||
}
|
||||
}
|
||||
|
||||
void nc_hashmap_free(NcHashmap *self)
|
||||
{
|
||||
if (!self) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < self->n_buckets; i++) {
|
||||
NcHashmapEntry *row = &(self->buckets[i]);
|
||||
nc_hashmap_free_bucket(self, row, true);
|
||||
}
|
||||
if (self->buckets) {
|
||||
free(self->buckets);
|
||||
}
|
||||
|
||||
free(self);
|
||||
}
|
||||
|
||||
static void nc_hashmap_update_next_size(NcHashmap *self)
|
||||
{
|
||||
if (!self) {
|
||||
return;
|
||||
}
|
||||
self->next_size = (int)(self->n_buckets * FULL_FACTOR);
|
||||
}
|
||||
|
||||
int nc_hashmap_size(NcHashmap *self)
|
||||
{
|
||||
if (!self) {
|
||||
return -1;
|
||||
}
|
||||
return self->size;
|
||||
}
|
||||
|
||||
static bool nc_hashmap_resize(NcHashmap *self)
|
||||
{
|
||||
if (!self || !self->buckets) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NcHashmapEntry *old_buckets = self->buckets;
|
||||
NcHashmapEntry *new_buckets = NULL;
|
||||
NcHashmapEntry *entry = NULL;
|
||||
int incr;
|
||||
|
||||
int old_size, new_size;
|
||||
int items = 0;
|
||||
|
||||
new_size = old_size = self->n_buckets;
|
||||
new_size *= INCREASE_FACTOR;
|
||||
|
||||
new_buckets = calloc(new_size, sizeof(NcHashmapEntry));
|
||||
if (!new_buckets) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < old_size; i++) {
|
||||
entry = &(old_buckets[i]);
|
||||
while (entry) {
|
||||
if (entry->occ) {
|
||||
unsigned hash = nc_hashmap_get_hash(self, entry->hash);
|
||||
if ((incr = nc_hashmap_insert_bucket(self,
|
||||
new_buckets,
|
||||
new_size,
|
||||
hash,
|
||||
entry->hash,
|
||||
entry->value)) > 0) {
|
||||
items += incr;
|
||||
} else {
|
||||
/* Likely a memory issue */
|
||||
goto failure;
|
||||
}
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
/* Successfully resized - do this separately because we need to
|
||||
* gaurantee old data is preserved */
|
||||
for (int i = 0; i < old_size; i++) {
|
||||
nc_hashmap_free_bucket(self, &(old_buckets[i]), false);
|
||||
}
|
||||
|
||||
free(old_buckets);
|
||||
self->n_buckets = new_size;
|
||||
self->size = items;
|
||||
self->buckets = new_buckets;
|
||||
|
||||
nc_hashmap_update_next_size(self);
|
||||
return true;
|
||||
|
||||
failure:
|
||||
for (int i = 0; i < new_size; i++) {
|
||||
nc_hashmap_free_bucket(self, &(new_buckets[i]), true);
|
||||
}
|
||||
free(new_buckets);
|
||||
return false;
|
||||
}
|
||||
|
||||
void nc_hashmap_iter_init(NcHashmap *map, NcHashmapIter *citer)
|
||||
{
|
||||
_NcHashmapIter *iter = NULL;
|
||||
if (!map || !citer) {
|
||||
return;
|
||||
}
|
||||
iter = (_NcHashmapIter *)citer;
|
||||
_NcHashmapIter it = {
|
||||
.bucket = -1, .map = map, .item = NULL,
|
||||
};
|
||||
*iter = it;
|
||||
}
|
||||
|
||||
bool nc_hashmap_iter_next(NcHashmapIter *citer, void **key, void **value)
|
||||
{
|
||||
_NcHashmapIter *iter = NULL;
|
||||
NcHashmapEntry *item = NULL;
|
||||
NcHashmap *map = NULL;
|
||||
int n_buckets = 0;
|
||||
|
||||
if (!citer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
iter = (_NcHashmapIter *)citer;
|
||||
map = iter->map;
|
||||
if (!map) {
|
||||
return false;
|
||||
}
|
||||
n_buckets = map->n_buckets;
|
||||
item = iter->item;
|
||||
|
||||
for (;;) {
|
||||
if (iter->bucket >= n_buckets) {
|
||||
if (item && !item->next) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!item) {
|
||||
iter->bucket++;
|
||||
if (iter->bucket > n_buckets - 1) {
|
||||
return false;
|
||||
}
|
||||
item = &(map->buckets[iter->bucket]);
|
||||
}
|
||||
if (item && item->occ) {
|
||||
goto success;
|
||||
}
|
||||
item = item->next;
|
||||
}
|
||||
return false;
|
||||
|
||||
success:
|
||||
iter->item = item->next;
|
||||
if (key) {
|
||||
*key = item->hash;
|
||||
}
|
||||
if (value) {
|
||||
*value = item->value;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Editor modelines - https://www.wireshark.org/tools/modelines.html
|
||||
*
|
||||
* Local variables:
|
||||
* c-basic-offset: 8
|
||||
* tab-width: 8
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*
|
||||
* vi: set shiftwidth=8 tabstop=8 expandtab:
|
||||
* :indentSize=8:tabSize=8:noTabs=true:
|
||||
*/
|
||||
@@ -1,239 +0,0 @@
|
||||
/*
|
||||
* This file is part of libnica.
|
||||
*
|
||||
* Copyright (C) 2016 Intel Corporation
|
||||
*
|
||||
* libnica is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This is a chained Hashmap implementation. It focuses on being clean
|
||||
* and efficient, and is comparable (at -O2) to open addressing hashmaps
|
||||
* up until around 105,000 elements, where open addressing will begin
|
||||
* to come out in front. At 1 million elements, open addressing is a clear
|
||||
* winner, however currently we only need a maximum of 70k elements in
|
||||
* our implementation.
|
||||
*
|
||||
* Given the memory required to even begin to deal with 1 million elements,
|
||||
* it becomes very questionable whether a hashmap should have even been
|
||||
* used in the first place (mmap'd db?)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
/* Convert between uint and void* */
|
||||
#define NC_HASH_KEY(x) ((void *)((uintptr_t)(x)))
|
||||
#define NC_HASH_VALUE(x) NC_HASH_KEY(x)
|
||||
#define NC_UNHASH_KEY(x) ((unsigned int)((uintptr_t)(x)))
|
||||
#define NC_UNHASH_VALUE(x) NC_UNHASH_KEY(x)
|
||||
|
||||
typedef struct NcHashmap NcHashmap;
|
||||
|
||||
/**
|
||||
* Iteration object
|
||||
*/
|
||||
typedef struct NcHashmapIter {
|
||||
int n0;
|
||||
void *n1;
|
||||
void *n2;
|
||||
} NcHashmapIter;
|
||||
|
||||
/**
|
||||
* Hash comparison function definition
|
||||
*
|
||||
* @param l First value to compare
|
||||
* @param r Second value to compare
|
||||
*
|
||||
* @return true if l and r both match, otherwise false
|
||||
*/
|
||||
typedef bool (*nc_hash_compare_func)(const void *l, const void *r);
|
||||
|
||||
/**
|
||||
* Hash creation function definition
|
||||
*
|
||||
* @param key Key to generate a hash for
|
||||
*
|
||||
* @return an unsigned integer hash result
|
||||
*/
|
||||
typedef unsigned (*nc_hash_create_func)(const void *key);
|
||||
|
||||
/**
|
||||
* Callback definition to free keys and values
|
||||
*
|
||||
* @param p Single-depth pointer to either a key or value that should be freed
|
||||
*/
|
||||
typedef void (*nc_hash_free_func)(void *p);
|
||||
|
||||
/**
|
||||
* Default hash/comparison functions
|
||||
*
|
||||
* @note These are only used for comparison of *keys*, not values. Unless
|
||||
* explicitly using string keys, you should most likely stick with the default
|
||||
* nc_simple_hash and nc_simple_compare functions
|
||||
*/
|
||||
|
||||
/* Default string hash */
|
||||
static inline unsigned nc_string_hash(const void *key)
|
||||
{
|
||||
unsigned hash = 5381;
|
||||
const signed char *c;
|
||||
|
||||
/* DJB's hash function */
|
||||
for (c = key; *c != '\0'; c++) {
|
||||
hash = (hash << 5) + hash + (unsigned)*c;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trivial pointer->uint hash
|
||||
*/
|
||||
static inline unsigned nc_simple_hash(const void *source)
|
||||
{
|
||||
return NC_UNHASH_KEY(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparison of string keys
|
||||
*/
|
||||
static inline bool nc_string_compare(const void *l, const void *r)
|
||||
{
|
||||
if (!l || !r) {
|
||||
return false;
|
||||
}
|
||||
return (strcmp(l, r) == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trivial pointer comparison
|
||||
*/
|
||||
static inline bool nc_simple_compare(const void *l, const void *r)
|
||||
{
|
||||
return (l == r);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new NcHashmap
|
||||
*
|
||||
* @param hash Hash creation function
|
||||
* @param compare Key comparison function
|
||||
*
|
||||
* @return A newly allocated NcHashmap
|
||||
*/
|
||||
NcHashmap *nc_hashmap_new(nc_hash_create_func hash, nc_hash_compare_func compare);
|
||||
|
||||
/**
|
||||
* Create a new NcHashmap with cleanup functions
|
||||
*
|
||||
* @param hash Hash creation function
|
||||
* @param compare Key comparison function
|
||||
* @param key_free Function to free keys when removed/destroyed
|
||||
* @param value_free Function to free values when removed/destroyed
|
||||
*
|
||||
* @return A newly allocated NcHashmap
|
||||
*/
|
||||
NcHashmap *nc_hashmap_new_full(nc_hash_create_func hash, nc_hash_compare_func compare,
|
||||
nc_hash_free_func key_free, nc_hash_free_func value_free);
|
||||
|
||||
/**
|
||||
* Store a key/value pair in the hashmap
|
||||
*
|
||||
* @note This will displace duplicate keys, and may free both the key
|
||||
* and value if key_free and value_free are non null
|
||||
*
|
||||
* @param key Key to store in the hashmap
|
||||
* @param value Value to be associated with the key
|
||||
*
|
||||
* @return true if the operation succeeded.
|
||||
*/
|
||||
bool nc_hashmap_put(NcHashmap *map, const void *key, void *value);
|
||||
|
||||
/**
|
||||
* Get the value associated with the unique key
|
||||
*
|
||||
* @param key Unique key to obtain a value for
|
||||
* @return The associated value if it exists, otherwise NULL
|
||||
*/
|
||||
void *nc_hashmap_get(NcHashmap *map, const void *key);
|
||||
|
||||
/**
|
||||
* Determine if the key has an associated value in the NcHashmap
|
||||
*
|
||||
* @param key Unique key to check value for
|
||||
* @return True if the key exists in the hashmap
|
||||
*/
|
||||
bool nc_hashmap_contains(NcHashmap *self, const void *key);
|
||||
|
||||
/**
|
||||
* Free the given NcHashmap, and all keys/values if appropriate
|
||||
*/
|
||||
void nc_hashmap_free(NcHashmap *map);
|
||||
|
||||
/**
|
||||
* Remove the value and key identified by key.
|
||||
*
|
||||
* @note If key_free or value_free are non-NULL, they will be invoked
|
||||
* for both the key and value being removed
|
||||
*
|
||||
* @param key The unique key to remove
|
||||
* @return true if the key/value pair were removed
|
||||
*/
|
||||
bool nc_hashmap_remove(NcHashmap *map, const void *key);
|
||||
|
||||
/**
|
||||
* Remove the value and key identified by key without freeing them
|
||||
*
|
||||
* @return true if the key/value pair were stolen
|
||||
*/
|
||||
bool nc_hashmap_steal(NcHashmap *map, const void *key);
|
||||
|
||||
/**
|
||||
* Return the current size of the hashmap
|
||||
*
|
||||
* @return size of the current hashmap (element count)
|
||||
*/
|
||||
int nc_hashmap_size(NcHashmap *map);
|
||||
|
||||
/**
|
||||
* Initialise a NcHashmapIter for iteration purposes
|
||||
*
|
||||
* @note The iter *must* be re-inited to re-use, or if it becomes
|
||||
* exhausted from a previous iteration run
|
||||
*
|
||||
* @param iter pointer to a NcHashmapIter
|
||||
*/
|
||||
void nc_hashmap_iter_init(NcHashmap *map, NcHashmapIter *iter);
|
||||
|
||||
/**
|
||||
* Iterate every key/value pair in the hashmap
|
||||
*
|
||||
* @param iter A correctly initialised NcHashmapIter
|
||||
* @param key Pointers to store the key in, may be NULL to skip
|
||||
* @param value Pointer to store the value in, may be NULL to skip
|
||||
*
|
||||
* @return true if it's possible to iterate
|
||||
*/
|
||||
bool nc_hashmap_iter_next(NcHashmapIter *iter, void **key, void **value);
|
||||
|
||||
DEF_AUTOFREE(NcHashmap, nc_hashmap_free)
|
||||
|
||||
/*
|
||||
* Editor modelines - https://www.wireshark.org/tools/modelines.html
|
||||
*
|
||||
* Local variables:
|
||||
* c-basic-offset: 8
|
||||
* tab-width: 8
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*
|
||||
* vi: set shiftwidth=8 tabstop=8 expandtab:
|
||||
* :indentSize=8:tabSize=8:noTabs=true:
|
||||
*/
|
||||
@@ -1,341 +0,0 @@
|
||||
/*
|
||||
* This file is part of libnica.
|
||||
*
|
||||
* Copyright (C) 2016 Intel Corporation
|
||||
*
|
||||
* libnica is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1
|
||||
* of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hashmap.h"
|
||||
#include "inifile.h"
|
||||
|
||||
/**
|
||||
* Mapping of @NcIniError to static strings
|
||||
*/
|
||||
static const char *_errors[] = {[NC_INI_ERROR_FILE] = "",
|
||||
[NC_INI_ERROR_EMPTY_KEY] = "Encountered empty key",
|
||||
[NC_INI_ERROR_NOT_CLOSED] = "Expected closing \']\' for section",
|
||||
[NC_INI_ERROR_NO_SECTION] =
|
||||
"Encountered key=value mapping without a valid section",
|
||||
[NC_INI_ERROR_INVALID_LINE] = "Expected key=value notation" };
|
||||
|
||||
const char *nc_ini_error(NcIniError error)
|
||||
{
|
||||
int j = abs(error);
|
||||
if (j < NC_INI_ERROR_FILE || j >= NC_INI_ERROR_MAX) {
|
||||
return "[Unknown Error]";
|
||||
}
|
||||
return _errors[j];
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip leading whitespace from string (left)
|
||||
*/
|
||||
static char *lstrip(char *str, size_t len, size_t *out_len)
|
||||
{
|
||||
size_t skip_len = 0;
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (str[i] == ' ' || str[i] == '\t') {
|
||||
++skip_len;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
*out_len = len - skip_len;
|
||||
if (skip_len > 0) {
|
||||
char *c = strdup(str + skip_len);
|
||||
free(str);
|
||||
return c;
|
||||
}
|
||||
return str + skip_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip trailing whitespace from string (right)
|
||||
*/
|
||||
static char *rstrip(char *str, size_t len, size_t *out_len)
|
||||
{
|
||||
size_t skip_len = 0;
|
||||
|
||||
for (int i = len; i > 0; i--) {
|
||||
if (str[i] == ' ' || str[i] == '\t') {
|
||||
++skip_len;
|
||||
continue;
|
||||
} else if (str[i] == '\0') {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
*out_len = len - skip_len;
|
||||
if (len == skip_len) {
|
||||
return str;
|
||||
}
|
||||
str[(len - skip_len)] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Chew both ends of a null terminated string of whitespace
|
||||
* If the left side is whitespace padded, this will always result
|
||||
* in a new allocation due to fast-forwarding the pointer. Keep
|
||||
* this in mind.
|
||||
*/
|
||||
static char *string_chew_terminated(char *inp)
|
||||
{
|
||||
int skip_offset = 0;
|
||||
int len = 0;
|
||||
int end_len = 0;
|
||||
bool count = true;
|
||||
|
||||
if (!inp) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (char *c = inp; *c; c++) {
|
||||
if (count && (*c == ' ' || *c == '\t')) {
|
||||
++skip_offset;
|
||||
continue;
|
||||
} else {
|
||||
count = false;
|
||||
}
|
||||
++len;
|
||||
}
|
||||
for (int i = len; i > skip_offset; i--) {
|
||||
if (inp[i] == ' ' || inp[i] == '\t') {
|
||||
++end_len;
|
||||
continue;
|
||||
} else if (inp[i] == '\0') {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (end_len > 0) {
|
||||
inp[(len - end_len)] = '\0';
|
||||
}
|
||||
if (skip_offset > 0) {
|
||||
char *c = strdup(inp + skip_offset);
|
||||
free(inp);
|
||||
return c;
|
||||
}
|
||||
return inp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Munch both ends of the string
|
||||
*/
|
||||
static char *string_strip(char *str, size_t len, size_t *out_len)
|
||||
{
|
||||
size_t mylen = len;
|
||||
|
||||
char *c = lstrip(str, len, &mylen);
|
||||
c = rstrip(c, mylen, &mylen);
|
||||
if (out_len) {
|
||||
*out_len = mylen;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
NcHashmap *nc_ini_file_parse(const char *path)
|
||||
{
|
||||
NcHashmap *ret = NULL;
|
||||
int error_line = 0;
|
||||
int r = 0;
|
||||
|
||||
r = nc_ini_file_parse_full(path, &ret, &error_line);
|
||||
if (r != 0) {
|
||||
if (abs(r) == NC_INI_ERROR_FILE) {
|
||||
fprintf(stderr, "[inifile] %s: %s\n", strerror(errno), path);
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"[inifile] %s [L%d]: %s\n",
|
||||
nc_ini_error(r),
|
||||
error_line,
|
||||
path);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int nc_ini_file_parse_full(const char *path, NcHashmap **out_map, int *error_line_number)
|
||||
{
|
||||
autofree(FILE) *file = NULL;
|
||||
char *buf = NULL;
|
||||
ssize_t r = 0;
|
||||
size_t sn = 0;
|
||||
int line_count = 1;
|
||||
char *current_section = NULL;
|
||||
NcHashmap *root_map = NULL;
|
||||
NcHashmap *section_map = NULL;
|
||||
bool failed = false;
|
||||
int err_ret = 0;
|
||||
|
||||
file = fopen(path, "r");
|
||||
if (!file) {
|
||||
return -(NC_INI_ERROR_FILE);
|
||||
}
|
||||
|
||||
if (!out_map) {
|
||||
fprintf(stderr, "nc_ini_file_parse_full(): NcHashmap pointer invalid\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
root_map = nc_hashmap_new_full(nc_string_hash,
|
||||
nc_string_compare,
|
||||
free,
|
||||
(nc_hash_free_func)nc_hashmap_free);
|
||||
|
||||
while ((r = getline(&buf, &sn, file)) != -1) {
|
||||
char *ch = NULL;
|
||||
size_t str_len = r;
|
||||
|
||||
/* Fix newline */
|
||||
if (buf[r - 1] == '\n') {
|
||||
buf[r - 1] = '\0';
|
||||
--r;
|
||||
}
|
||||
str_len = r;
|
||||
|
||||
buf = string_strip(buf, r, &str_len);
|
||||
/* Empty lines are fine */
|
||||
if (streq(buf, "")) {
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (buf[0] == '[') {
|
||||
/* Validate section start */
|
||||
if (buf[str_len - 1] != ']') {
|
||||
/* Throw error */
|
||||
err_ret = NC_INI_ERROR_NOT_CLOSED;
|
||||
goto fail;
|
||||
}
|
||||
/* Grab the section name, and "close" last section */
|
||||
buf[str_len - 1] = '\0';
|
||||
if (current_section) {
|
||||
free(current_section);
|
||||
}
|
||||
current_section = strdup(buf + 1);
|
||||
section_map = nc_hashmap_get(root_map, current_section);
|
||||
if (!section_map) {
|
||||
/* Create a new section dynamically */
|
||||
section_map = nc_hashmap_new_full(nc_string_hash,
|
||||
nc_string_compare,
|
||||
free,
|
||||
free);
|
||||
nc_hashmap_put(root_map, strdup(current_section), section_map);
|
||||
}
|
||||
goto next;
|
||||
} else if (buf[0] == '#' || buf[0] == ';') {
|
||||
/* Skip comment */
|
||||
goto next;
|
||||
}
|
||||
|
||||
/* Look for key = value */
|
||||
ch = strchr(buf, '=');
|
||||
if (!ch) {
|
||||
/* Throw error */
|
||||
err_ret = NC_INI_ERROR_INVALID_LINE;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Can't have sectionless k->v */
|
||||
if (!current_section) {
|
||||
err_ret = NC_INI_ERROR_NO_SECTION;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
int offset = ch - buf;
|
||||
|
||||
/* Grab the key->value from this assignment line */
|
||||
char *value = strdup((buf + offset) + 1);
|
||||
buf[offset] = '\0';
|
||||
char *key = strdup(buf);
|
||||
key = string_chew_terminated(key);
|
||||
value = string_chew_terminated(value);
|
||||
|
||||
if (streq(key, "")) {
|
||||
err_ret = NC_INI_ERROR_EMPTY_KEY;
|
||||
free(key);
|
||||
free(value);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Ensure a section mapping exists */
|
||||
section_map = nc_hashmap_get(root_map, current_section);
|
||||
if (!section_map) {
|
||||
err_ret = 1;
|
||||
fprintf(stderr,
|
||||
"[inifile] Fatal! No section map for named "
|
||||
"section: %s\n",
|
||||
current_section);
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Insert these guys into the map */
|
||||
if (!nc_hashmap_put(section_map, key, value)) {
|
||||
err_ret = 1;
|
||||
fprintf(stderr, "[inifile] Fatal! Out of memory\n");
|
||||
abort();
|
||||
}
|
||||
/* Progression + cleanup */
|
||||
next:
|
||||
if (buf) {
|
||||
free(buf);
|
||||
buf = NULL;
|
||||
}
|
||||
++line_count;
|
||||
sn = 0;
|
||||
continue;
|
||||
/* Parsing error, bail */
|
||||
fail:
|
||||
failed = true;
|
||||
if (error_line_number) {
|
||||
*error_line_number = line_count;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (buf) {
|
||||
free(buf);
|
||||
buf = NULL;
|
||||
}
|
||||
|
||||
if (current_section) {
|
||||
free(current_section);
|
||||
current_section = NULL;
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
if (root_map) {
|
||||
nc_hashmap_free(root_map);
|
||||
}
|
||||
return -(err_ret);
|
||||
}
|
||||
*out_map = root_map;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Editor modelines - https://www.wireshark.org/tools/modelines.html
|
||||
*
|
||||
* Local variables:
|
||||
* c-basic-offset: 8
|
||||
* tab-width: 8
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*
|
||||
* vi: set shiftwidth=8 tabstop=8 expandtab:
|
||||
* :indentSize=8:tabSize=8:noTabs=true:
|
||||
*/
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* This file is part of libnica.
|
||||
*
|
||||
* Copyright (C) 2016 Intel Corporation
|
||||
*
|
||||
* libnica is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1
|
||||
* of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hashmap.h"
|
||||
#include "util.h"
|
||||
|
||||
/**
|
||||
* The Nica INI Parser focuses on simplicity and ease of use, therefore it
|
||||
* currently only supports a read-only parse operation.
|
||||
*
|
||||
* The parse functions are used to return a _root_ @NcHashmap which contains
|
||||
* name to section mappings. Each section is in turn an @NcHashmap with string
|
||||
* to string mappings.
|
||||
*
|
||||
* Consider the following example:
|
||||
*
|
||||
* [Person]
|
||||
* alive = true
|
||||
* ; This person is alive
|
||||
*
|
||||
* The returned @NcHashmap will contain the key "Person", which corresponds to
|
||||
* another @NcHashmap. This @NcHashmap will contain the key "alive", which has
|
||||
* the value "true". Therefore we can access this variable using the @NcHashmap
|
||||
* API:
|
||||
*
|
||||
* char *alive = nc_hashmap_get(nc_hashmap_get(ini, "Person"), "alive");
|
||||
*
|
||||
* Conversely, we can use the API to determine whether a section or key is set:
|
||||
*
|
||||
* if (nc_hashmap_contains(ini, "Person"))
|
||||
*
|
||||
* As @NcHashmap returns NULL in nc_hashmap_get, you can happily pass NULL
|
||||
* keys, allowing quick key fetches without checking if the section exists.
|
||||
*
|
||||
* In our INI file, lines beginning with ";" or "#" are considered as comments
|
||||
* and are not processed. A line following the "key = value" notation is an
|
||||
* _assignment_. "[Section]" is considered to be a section in the INI file, and
|
||||
* all INI files must have at least one section.
|
||||
*
|
||||
* We do allow duplication section definitions, by following a merge policy. If
|
||||
* a key is redefined then the original key->value mapping is freed and the new
|
||||
* key->value mapping is inserted.
|
||||
*
|
||||
* The parser will return a correctly configured @NcHashmap, the only cleanup
|
||||
* required is to call nc_hashmap_free on it. Alternatively, use the autofree
|
||||
* helper, for a RAII approach:
|
||||
*
|
||||
* autofree(NcHashmap) *map = nc_ini_file_parse("myfile.ini");
|
||||
*/
|
||||
|
||||
/**
|
||||
* Errors that are reported when parsing an ini file
|
||||
*/
|
||||
typedef enum {
|
||||
NC_INI_ERROR_MIN = 0,
|
||||
NC_INI_ERROR_FILE, /**< File based error, check strerror(errno) */
|
||||
NC_INI_ERROR_EMPTY_KEY, /**< Empty key in an assignment line */
|
||||
NC_INI_ERROR_NOT_CLOSED, /**< Encountered section start that wasn't closed with a ']' */
|
||||
NC_INI_ERROR_NO_SECTION, /**< Key assignment with no defined sections */
|
||||
NC_INI_ERROR_INVALID_LINE, /**< Encountered an invalid line (syntax) */
|
||||
NC_INI_ERROR_MAX
|
||||
} NcIniError;
|
||||
|
||||
/**
|
||||
* Convenience wrapper for nc_ini_file_parse_full, reports errors to stderrr
|
||||
* and returns an NcHashmap if the parsing succeeded
|
||||
*/
|
||||
NcHashmap *nc_ini_file_parse(const char *path);
|
||||
|
||||
/**
|
||||
* Parse an INI file into a hash of hashes.
|
||||
*
|
||||
* @param path Path on the filesystem to parse, must be in INI syntax.
|
||||
* @param out_map Pointer to store the resulting root NcHashmap in
|
||||
* @param error_line_number If not null, then the erronous line number is
|
||||
* stored.
|
||||
*
|
||||
* @return 0 if the call was succesful, or a negative integer. See nc_ini_error
|
||||
*/
|
||||
int nc_ini_file_parse_full(const char *path, NcHashmap **out_map, int *error_line_number);
|
||||
|
||||
/**
|
||||
* Return a string representation for a given @NcIniError
|
||||
*
|
||||
* @param error Valid NcIniError code
|
||||
* @return a static string owned by the implementation
|
||||
*/
|
||||
const char *nc_ini_error(NcIniError error);
|
||||
|
||||
/*
|
||||
* Editor modelines - https://www.wireshark.org/tools/modelines.html
|
||||
*
|
||||
* Local variables:
|
||||
* c-basic-offset: 8
|
||||
* tab-width: 8
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*
|
||||
* vi: set shiftwidth=8 tabstop=8 expandtab:
|
||||
* :indentSize=8:tabSize=8:noTabs=true:
|
||||
*/
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* This file is part of libnica.
|
||||
*
|
||||
* Copyright (C) 2016 Intel Corporation
|
||||
*
|
||||
* libnica is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1
|
||||
* of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define DEF_AUTOFREE(N, C) \
|
||||
static inline void _autofree_func_##N(void *p) \
|
||||
{ \
|
||||
if (p && *(N **)p) { \
|
||||
C(*(N **)p); \
|
||||
(*(void **)p) = NULL; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define autofree(N) __attribute__((cleanup(_autofree_func_##N))) N
|
||||
|
||||
/**
|
||||
* Dump any leaked file descriptors
|
||||
*/
|
||||
void nc_dump_file_descriptor_leaks(void);
|
||||
|
||||
#define streq(x, y) strcmp(x, y) == 0 ? true : false
|
||||
|
||||
DEF_AUTOFREE(char, free)
|
||||
DEF_AUTOFREE(FILE, fclose)
|
||||
|
||||
/*
|
||||
* Editor modelines - https://www.wireshark.org/tools/modelines.html
|
||||
*
|
||||
* Local variables:
|
||||
* c-basic-offset: 8
|
||||
* tab-width: 8
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*
|
||||
* vi: set shiftwidth=8 tabstop=8 expandtab:
|
||||
* :indentSize=8:tabSize=8:noTabs=true:
|
||||
*/
|
||||
@@ -0,0 +1,21 @@
|
||||
if shim_behaviour == 'replacement'
|
||||
shim_target_name = 'steam'
|
||||
else
|
||||
shim_target_name = 'lsi-steam'
|
||||
endif
|
||||
|
||||
if shim_behaviour != 'none'
|
||||
shim_sources = [
|
||||
'shim.c',
|
||||
]
|
||||
|
||||
shim = executable(
|
||||
shim_target_name,
|
||||
sources: shim_sources + nica_sources,
|
||||
include_directories: nica_includes,
|
||||
dependencies: [
|
||||
link_lsi,
|
||||
],
|
||||
install: true,
|
||||
)
|
||||
endif
|
||||
+33
-7
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of linux-steam-integration.
|
||||
*
|
||||
* Copyright © 2016 Ikey Doherty
|
||||
* Copyright © 2016-2017 Ikey Doherty <ikey@solus-project.com>
|
||||
*
|
||||
* linux-steam-integration is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License as
|
||||
@@ -26,16 +26,29 @@
|
||||
*/
|
||||
#define EMUL32BIN "linux32"
|
||||
|
||||
/**
|
||||
* Audit path is used for the libintercept library to ensure Steam only uses the
|
||||
* host SDL, etc.
|
||||
*/
|
||||
#define AUDIT_PATH "/usr/\$LIB/liblsi-intercept.so"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
LsiConfig config = { 0 };
|
||||
bool is_x86_64;
|
||||
const char *n_argv[argc + 2];
|
||||
const char *exec_command = NULL;
|
||||
/* Use the appropriate Steam depending on configuration */
|
||||
const char *lsi_exec_bin = NULL;
|
||||
int i = 1;
|
||||
int8_t off = 0;
|
||||
int (*vfunc)(const char *, char *const argv[]) = NULL;
|
||||
|
||||
#ifdef REPLACE_STEAM
|
||||
lsi_exec_bin = STEAM_BINARY;
|
||||
#else
|
||||
lsi_exec_bin = "/usr/bin/steam";
|
||||
#endif
|
||||
/* Initialise config */
|
||||
if (!lsi_config_load(&config)) {
|
||||
lsi_config_load_defaults(&config);
|
||||
@@ -43,8 +56,8 @@ int main(int argc, char **argv)
|
||||
|
||||
is_x86_64 = lsi_system_is_64bit();
|
||||
|
||||
if (!lsi_file_exists(STEAM_BINARY)) {
|
||||
lsi_report_failure("Steam isn't currently installed at %s", STEAM_BINARY);
|
||||
if (!lsi_file_exists(lsi_exec_bin)) {
|
||||
lsi_report_failure("Steam isn't currently installed at %s", lsi_exec_bin);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@@ -52,6 +65,12 @@ int main(int argc, char **argv)
|
||||
if (config.use_native_runtime) {
|
||||
/* Explicitly disable the runtime */
|
||||
setenv("STEAM_RUNTIME", "0", 1);
|
||||
#ifdef HAVE_LIBINTERCEPT
|
||||
/* Only use libintercept in combination with native runtime! */
|
||||
if (config.use_libintercept) {
|
||||
setenv("LD_AUDIT", AUDIT_PATH, 1);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
/* Only preload when needed. */
|
||||
if (lsi_system_requires_preload()) {
|
||||
@@ -64,20 +83,27 @@ int main(int argc, char **argv)
|
||||
* usage of dbus by Steam. Help them out */
|
||||
setenv("DBUS_FATAL_WARNINGS", "0", 1);
|
||||
|
||||
/* A recent regression is to interpret XMODIFIERS and then fail to
|
||||
* make it beyond `SDL_InitSubSystem` due to `InitIME` failing to
|
||||
* properly use D-BUS ...
|
||||
*/
|
||||
unsetenv("XMODIFIERS");
|
||||
unsetenv("GTK_MODULES");
|
||||
|
||||
memset(&n_argv, 0, sizeof(char *) * (argc + 2));
|
||||
|
||||
/* If we're 64-bit and 32-bit is forced, proxy via linux32 */
|
||||
if (config.force_32 && is_x86_64) {
|
||||
exec_command = EMUL32BIN;
|
||||
n_argv[0] = EMUL32BIN;
|
||||
n_argv[1] = STEAM_BINARY;
|
||||
n_argv[1] = lsi_exec_bin;
|
||||
off = 1;
|
||||
/* Use linux32 in the path */
|
||||
vfunc = execvp;
|
||||
} else {
|
||||
/* Directly call STEAM_BINARY */
|
||||
exec_command = STEAM_BINARY;
|
||||
n_argv[0] = STEAM_BINARY;
|
||||
/* Directly call lsi_exec_bin */
|
||||
exec_command = lsi_exec_bin;
|
||||
n_argv[0] = lsi_exec_bin;
|
||||
/* Full path here due to shadow nature */
|
||||
vfunc = execv;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
env -i DBUS_SESSION_ADDRESS=$DBUS_SESSION_ADDRESS \
|
||||
PATH=$PATH \
|
||||
DISPLAY=$DISPLAY \
|
||||
HOME=$HOME \
|
||||
USER=$USER \
|
||||
USERNAME=$USERNAME \
|
||||
LD_AUDIT="`pwd`/buildroot/install/usr/\$LIB/liblsi-intercept.so" \
|
||||
LD_PRELOAD="/usr/\$LIB/libxcb.so.1:/usr/\$LIB/libX11.so.6:/usr/\$LIB/libstdc++.so.6" \
|
||||
DBUS_FATAL_WARNINGS=0 \
|
||||
STEAM_RUNTIME=0 /usr/lib64/steam/steam
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
#!/bin/bash
|
||||
clang-format -i $(find . -name '*.[ch]')
|
||||
clang-format -i $(find . -not -path '*/libnica/*' -name '*.[ch]')
|
||||
|
||||
Reference in New Issue
Block a user