コード例 #1
0
ファイル: util.c プロジェクト: Fresne/i3-1
/*
 * exec()s an i3 utility, for example the config file migration script or
 * i3-nagbar. This function first searches $PATH for the given utility named,
 * then falls back to the dirname() of the i3 executable path and then falls
 * back to the dirname() of the target of /proc/self/exe (on linux).
 *
 * This function should be called after fork()ing.
 *
 * The first argument of the given argv vector will be overwritten with the
 * executable name, so pass NULL.
 *
 * If the utility cannot be found in any of these locations, it exits with
 * return code 2.
 *
 */
void exec_i3_utility(char *name, char *argv[]) {
    /* start the migration script, search PATH first */
    char *migratepath = name;
    argv[0] = migratepath;
    execvp(migratepath, argv);

    /* if the script is not in path, maybe the user installed to a strange
     * location and runs the i3 binary with an absolute path. We use
     * argv[0]’s dirname */
    char *pathbuf = strdup(start_argv[0]);
    char *dir = dirname(pathbuf);
    sasprintf(&migratepath, "%s/%s", dir, name);
    argv[0] = migratepath;
    execvp(migratepath, argv);

#if defined(__linux__)
    /* on linux, we have one more fall-back: dirname(/proc/self/exe) */
    char buffer[BUFSIZ];
    if (readlink("/proc/self/exe", buffer, BUFSIZ) == -1) {
        warn("could not read /proc/self/exe");
        _exit(1);
    }
    dir = dirname(buffer);
    sasprintf(&migratepath, "%s/%s", dir, name);
    argv[0] = migratepath;
    execvp(migratepath, argv);
#endif

    warn("Could not start %s", name);
    _exit(2);
}
コード例 #2
0
ファイル: outputs.c プロジェクト: fernandoataoldotcom/i3
/*
 * Parse a string (name)
 *
 */
static int outputs_string_cb(void *params_, const unsigned char *val, size_t len) {
    struct outputs_json_params *params = (struct outputs_json_params *)params_;

    if (!strcmp(params->cur_key, "current_workspace")) {
        char *copy = NULL;
        sasprintf(&copy, "%.*s", len, val);

        char *end;
        errno = 0;
        long parsed_num = strtol(copy, &end, 10);
        if (errno == 0 &&
            (end && *end == '\0'))
            params->outputs_walk->ws = parsed_num;

        FREE(copy);
        FREE(params->cur_key);
        return 1;
    }

    if (strcmp(params->cur_key, "name")) {
        return 0;
    }

    sasprintf(&(params->outputs_walk->name), "%.*s", len, val);

    FREE(params->cur_key);
    return 1;
}
コード例 #3
0
ファイル: cheat.cpp プロジェクト: BG1/warzone2100
void recvProcessDebugMappings(NETQUEUE queue)
{
	bool val = false;
	NETbeginDecode(queue, GAME_DEBUG_MODE);
		NETbool(&val);
	NETend();

	bool oldDebugMode = getDebugMappingStatus();
	processDebugMappings(queue.index, val);
	bool newDebugMode = getDebugMappingStatus();

	char const *cmsg;
	if (val)
	{
		sasprintf((char**)&cmsg, _("%s wants to enable debug mode. Enabled: %s, Disabled: %s."), getPlayerName(queue.index), getWantedDebugMappingStatuses(true).c_str(), getWantedDebugMappingStatuses(false).c_str());
	}
	else
	{
		sasprintf((char**)&cmsg, _("%s wants to disable debug mode. Enabled: %s, Disabled: %s."), getPlayerName(queue.index), getWantedDebugMappingStatuses(true).c_str(), getWantedDebugMappingStatuses(false).c_str());
	}
	addConsoleMessage(cmsg, DEFAULT_JUSTIFY,  SYSTEM_MESSAGE);

	if (!oldDebugMode && newDebugMode)
	{
		addConsoleMessage(_("Debug mode now enabled!"), DEFAULT_JUSTIFY,  SYSTEM_MESSAGE);
	}
	else if (oldDebugMode && !newDebugMode)
	{
		addConsoleMessage(_("Debug mode now disabled!"), DEFAULT_JUSTIFY,  SYSTEM_MESSAGE);
	}
}
コード例 #4
0
ファイル: display_version.c プロジェクト: Acidburn0zzz/i3
static int version_string(void *ctx, const unsigned char *val, size_t len) {
    if (human_readable_key)
        sasprintf(&human_readable_version, "%.*s", (int)len, val);
    if (loaded_config_file_name_key)
        sasprintf(&loaded_config_file_name, "%.*s", (int)len, val);
    return 1;
}
コード例 #5
0
ファイル: main.c プロジェクト: Chr1stoph/i3
/*
 * Called when the user releases the mouse button. Checks whether the
 * coordinates are over a button and executes the appropriate action.
 *
 */
static void handle_button_release(xcb_connection_t *conn, xcb_button_release_event_t *event) {
    printf("button released on x = %d, y = %d\n",
           event->event_x, event->event_y);
    /* If the user hits the close button, we exit(0) */
    if (event->event_x >= (rect.width - logical_px(32)))
        exit(0);
    button_t *button = get_button_at(event->event_x, event->event_y);
    if (!button)
        return;

    /* We need to create a custom script containing our actual command
     * since not every terminal emulator which is contained in
     * i3-sensible-terminal supports -e with multiple arguments (and not
     * all of them support -e with one quoted argument either).
     *
     * NB: The paths need to be unique, that is, don’t assume users close
     * their nagbars at any point in time (and they still need to work).
     * */
    char *script_path = get_process_filename("nagbar-cmd");

    int fd = open(script_path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
    if (fd == -1) {
        warn("Could not create temporary script to store the nagbar command");
        return;
    }
    FILE *script = fdopen(fd, "w");
    if (script == NULL) {
        warn("Could not fdopen() temporary script to store the nagbar command");
        return;
    }
    fprintf(script, "#!/bin/sh\nrm %s\n%s", script_path, button->action);
    /* Also closes fd */
    fclose(script);

    char *link_path;
    char *exe_path = get_exe_path(argv0);
    sasprintf(&link_path, "%s.nagbar_cmd", script_path);
    if (symlink(exe_path, link_path) == -1) {
        err(EXIT_FAILURE, "Failed to symlink %s to %s", link_path, exe_path);
    }

    char *terminal_cmd;
    sasprintf(&terminal_cmd, "i3-sensible-terminal -e %s", link_path);
    printf("argv0 = %s\n", argv0);
    printf("terminal_cmd = %s\n", terminal_cmd);

    start_application(terminal_cmd);

    free(link_path);
    free(terminal_cmd);
    free(script_path);
    free(exe_path);

    /* TODO: unset flag, re-render */
}
コード例 #6
0
ファイル: get_exe_path.c プロジェクト: DSMan195276/i3
/*
 * This function returns the absolute path to the executable it is running in.
 *
 * The implementation follows http://stackoverflow.com/a/933996/712014
 *
 */
const char *get_exe_path(const char *argv0) {
	static char destpath[PATH_MAX];
	char tmp[PATH_MAX];

#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
	/* Linux and Debian/kFreeBSD provide /proc/self/exe */
#if defined(__linux__) || defined(__FreeBSD_kernel__)
	const char *exepath = "/proc/self/exe";
#elif defined(__FreeBSD__)
	const char *exepath = "/proc/curproc/file";
#endif
	ssize_t linksize;

	if ((linksize = readlink(exepath, destpath, sizeof(destpath) - 1)) != -1) {
		/* readlink() does not NULL-terminate strings, so we have to. */
		destpath[linksize] = '\0';

		return destpath;
	}
#endif

	/* argv[0] is most likely a full path if it starts with a slash. */
	if (argv0[0] == '/')
		return argv0;

	/* if argv[0] contains a /, prepend the working directory */
	if (strchr(argv0, '/') != NULL &&
		getcwd(tmp, sizeof(tmp)) != NULL) {
		snprintf(destpath, sizeof(destpath), "%s/%s", tmp, argv0);
		return destpath;
	}

	/* Fall back to searching $PATH (or _CS_PATH in absence of $PATH). */
	char *path = getenv("PATH");
	if (path == NULL) {
		/* _CS_PATH is typically something like "/bin:/usr/bin" */
		confstr(_CS_PATH, tmp, sizeof(tmp));
		sasprintf(&path, ":%s", tmp);
	} else {
		path = strdup(path);
	}
	const char *component;
	char *str = path;
	while (1) {
		if ((component = strtok(str, ":")) == NULL)
			break;
		str = NULL;
		snprintf(destpath, sizeof(destpath), "%s/%s", component, argv0);
		/* Of course this is not 100% equivalent to actually exec()ing the
		 * binary, but meh. */
		if (access(destpath, X_OK) == 0) {
			free(path);
			return destpath;
		}
	}
	free(path);

	/* Last resort: maybe it’s in /usr/bin? */
	return "/usr/bin/i3-nagbar";
}
コード例 #7
0
ファイル: config_parser.c プロジェクト: smrt28/i3
/*
 * Pushes a string (identified by 'identifier') on the stack. We simply use a
 * single array, since the number of entries we have to store is very small.
 *
 */
static void push_string(const char *identifier, const char *str) {
    for (int c = 0; c < 10; c++) {
        if (stack[c].identifier != NULL &&
            strcmp(stack[c].identifier, identifier) != 0)
            continue;
        if (stack[c].identifier == NULL) {
            /* Found a free slot, let’s store it here. */
            stack[c].identifier = identifier;
            stack[c].val.str = sstrdup(str);
            stack[c].type = STACK_STR;
        } else {
            /* Append the value. */
            char *prev = stack[c].val.str;
            sasprintf(&(stack[c].val.str), "%s,%s", prev, str);
            free(prev);
        }
        return;
    }

    /* When we arrive here, the stack is full. This should not happen and
     * means there’s either a bug in this parser or the specification
     * contains a command with more than 10 identified tokens. */
    fprintf(stderr, "BUG: commands_parser stack full. This means either a bug "
                    "in the code, or a new command which contains more than "
                    "10 identified tokens.\n");
    exit(1);
}
コード例 #8
0
ファイル: seqdisp.c プロジェクト: cybersphinx/wzgraphicsmods
//full screenvideo functions
static bool seq_StartFullScreenVideo(const char* videoName, const char* audioName, VIDEO_RESOLUTION resolution)
{
	const char* aAudioName = NULL;
	int chars_printed;

	bHoldSeqForAudio = false;

	chars_printed = ssprintf(aVideoName, "%s%s", aHardPath, videoName);
	ASSERT(chars_printed < sizeof(aVideoName), "sequence path + name greater than max string");

	//set audio path
	if (audioName != NULL)
	{
		sasprintf((char**)&aAudioName, "sequenceaudio/%s", audioName);
	}

	cdAudio_Pause();
	iV_SetFont(font_regular);
	iV_SetTextColour(WZCOL_TEXT_BRIGHT);

	/* We do not want to enter loop_SetVideoPlaybackMode() when we are
	 * doing intelligence videos.
	 */
	if (resolution == VIDEO_USER_CHOSEN_RESOLUTION)
	{
		//start video mode
		if (loop_GetVideoMode() == 0)
		{
			// check to see if we need to pause, and set font each time
			cdAudio_Pause();
			loop_SetVideoPlaybackMode();
			iV_SetFont(font_regular);
			iV_SetTextColour(WZCOL_TEXT_BRIGHT);
		}

		// set the dimensions to show full screen or native or ...
		seq_SetUserResolution();
	}

	if (!seq_Play(aVideoName))
	{
		seq_Shutdown();
		return false;
	}

	if (audioName == NULL)
	{
		bAudioPlaying = false;
	}
	else
	{
		// NOT controlled by sliders for now?
		static const float maxVolume = 1.f;

		bAudioPlaying = audio_PlayStream(aAudioName, maxVolume, NULL, NULL) ? true : false;
		ASSERT(bAudioPlaying == true, "unable to initialise sound %s", aAudioName);
	}

	return true;
}
コード例 #9
0
ファイル: main.c プロジェクト: DSMan195276/i3
static char *next_state(const cmdp_token *token) {
    cmdp_state _next_state = token->next_state;

    if (token->next_state == __CALL) {
        const char *modifiers = get_string("modifiers");
        int keycode = atoi(get_string("key"));
        int level = 0;
        if (modifiers != NULL &&
            strstr(modifiers, "Shift") != NULL) {
            /* When shift is included, we really need to use the second-level
             * symbol (upper-case). The lower-case symbol could be on a
             * different key than the upper-case one (unlikely for letters, but
             * more likely for special characters). */
            level = 1;

            /* Try to use the keysym on the first level (lower-case). In case
             * this doesn’t make it ambiguous (think of a keyboard layout
             * having '1' on two different keys, but '!' only on keycode 10),
             * we’ll stick with the keysym of the first level.
             *
             * This reduces a lot of confusion for users who switch keyboard
             * layouts from qwerty to qwertz or other slight variations of
             * qwerty (yes, that happens quite often). */
            KeySym sym = XkbKeycodeToKeysym(dpy, keycode, 0, 0);
            if (!keysym_used_on_other_key(sym, keycode))
                level = 0;
        }
        KeySym sym = XkbKeycodeToKeysym(dpy, keycode, 0, level);
        char *str = XKeysymToString(sym);
        const char *release = get_string("release");
        char *res;
        char *modrep = (modifiers == NULL ? sstrdup("") : sstrdup(modifiers));
        char *comma;
        while ((comma = strchr(modrep, ',')) != NULL) {
            *comma = '+';
        }
        sasprintf(&res, "bindsym %s%s%s %s%s\n", (modifiers == NULL ? "" : modrep), (modifiers == NULL ? "" : "+"), str, (release == NULL ? "" : release), get_string("command"));
        clear_stack();
        return res;
    }

    state = _next_state;

    /* See if we are jumping back to a state in which we were in previously
     * (statelist contains INITIAL) and just move statelist_idx accordingly. */
    for (int i = 0; i < statelist_idx; i++) {
        if (statelist[i] != _next_state)
            continue;
        statelist_idx = i+1;
        return NULL;
    }

    /* Otherwise, the state is new and we add it to the list */
    statelist[statelist_idx++] = _next_state;
    return NULL;
}
コード例 #10
0
ファイル: mime_child.c プロジェクト: ryo/netbsd-src
PUBLIC int
mime_run_command(const char *cmd, FILE *fo)
{
	sigset_t nset;
	FILE *nfo;
	pid_t pid;
	int p[2];
	int flags;

	if (cmd == NULL)
		return 0;

	flags = get_cmd_flags(cmd, &cmd);
	if (fo == NULL)		/* no output file, just return the flags! */
		return flags;

	if ((flags & CMD_FLAG_SHELLCMD) != 0) {	/* run command under the shell */
		char *cp;
		char *shellcmd;
		if ((shellcmd = value(ENAME_SHELL)) == NULL)
			shellcmd = __UNCONST(_PATH_CSHELL);
		(void)sasprintf(&cp, "%s -c '%s'", shellcmd, cmd);
		cmd = cp;
	}
	if (prepare_pipe(&nset, p) != 0) {
		warn("mime_run_command: prepare_pipe");
		return flags;	/* XXX - this or -1? */
	}
	flush_files(fo, 0); /* flush fo, all registered files, and stdout */

	switch (pid = start_command(cmd, &nset, p[READ], fileno(fo), NULL)) {
	case -1:	/* error */
		/* start_command already did a warn(). */
		warnx("mime_run_command: %s", cmd); /* tell a bit more */
		(void)close(p[READ]);
		(void)close(p[WRITE]);
		return flags;			/* XXX - this or -1? */

	case 0:		/* child */
		assert(/*CONSTCOND*/ 0);	/* a real coding error! */
		/* NOTREACHED */

	default:	/* parent */
		(void)close(p[READ]);

		nfo = fdopen(p[WRITE], "w");
		if (nfo == NULL) {
			warn("mime_run_command: fdopen");
			(void)close(p[WRITE]);
			warn("fdopen");
			return flags;
		}
		register_file(nfo, 1, pid);
		return flags;
	}
}
コード例 #11
0
ファイル: version.cpp プロジェクト: DylanHsu/warzone2100
const char* version_getFormattedVersionString()
{
	static char versionString[MAX_STR_LENGTH] = {'\0'};

	if (versionString[0] == '\0')
	{
		// Compose the working copy state string
#if (SVN_WC_MODIFIED && SVN_WC_SWITCHED)
		const char* wc_state = _(" (modified and switched locally)");
#elif (SVN_WC_MODIFIED)
		const char* wc_state = _(" (modified locally)");
#elif (SVN_WC_SWITCHED)
		const char* wc_state = _(" (switched locally)");
#else
		const char* wc_state = "";
#endif

		// Compose the build type string
#ifdef DEBUG
		const char* build_type = _(" - DEBUG");
#else
		const char* build_type = "";
#endif

		const char* build_date = NULL;

		if (strncmp(svn_uri_cstr, "tags/", strlen("tags/")) != 0)
		{
			sasprintf((char**)&build_date, _(" - Built %s"), version_getBuildDate());
		}
		else
		{
			build_date = "";
		}

		// Construct the version string
		// TRANSLATORS: This string looks as follows when expanded.
		// "Version <version name/number> <working copy state><BUILD DATE><BUILD TYPE>"
		snprintf(versionString, MAX_STR_LENGTH, _("Version %s%s%s%s"), version_getVersionString(), wc_state, build_date, build_type);
	}

	return versionString;
}
コード例 #12
0
ファイル: fake_outputs.c プロジェクト: Fresne/i3-1
/*
 * Creates outputs according to the given specification.
 * The specification must be in the format wxh+x+y, for example 1024x768+0+0,
 * with multiple outputs separated by commas:
 *   1900x1200+0+0,1280x1024+1900+0
 *
 */
void fake_outputs_init(const char *output_spec) {
    char useless_buffer[1024];
    const char *walk = output_spec;
    unsigned int x, y, width, height;
    while (sscanf(walk, "%ux%u+%u+%u", &width, &height, &x, &y) == 4) {
        DLOG("Parsed output as width = %u, height = %u at (%u, %u)\n",
             width, height, x, y);
        Output *new_output = get_screen_at(x, y);
        if (new_output != NULL) {
            DLOG("Re-used old output %p\n", new_output);
            /* This screen already exists. We use the littlest screen so that the user
               can always see the complete workspace */
            new_output->rect.width = min(new_output->rect.width, width);
            new_output->rect.height = min(new_output->rect.height, height);
        } else {
            new_output = scalloc(sizeof(Output));
            sasprintf(&(new_output->name), "fake-%d", num_screens);
            DLOG("Created new fake output %s (%p)\n", new_output->name, new_output);
            new_output->active = true;
            new_output->rect.x = x;
            new_output->rect.y = y;
            new_output->rect.width = width;
            new_output->rect.height = height;
            /* We always treat the screen at 0x0 as the primary screen */
            if (new_output->rect.x == 0 && new_output->rect.y == 0)
                TAILQ_INSERT_HEAD(&outputs, new_output, outputs);
            else
                TAILQ_INSERT_TAIL(&outputs, new_output, outputs);
            output_init_con(new_output);
            init_ws_for_output(new_output, output_get_content(new_output->con));
            num_screens++;
        }

        /* Figure out how long the input was to skip it */
        walk += sprintf(useless_buffer, "%ux%u+%u+%u", width, height, x, y) + 1;
    }

    if (num_screens == 0) {
        ELOG("No screens found. Please fix your setup. i3 will exit now.\n");
        exit(0);
    }
}
コード例 #13
0
ALenum __sound_GetContextError(ALCdevice* device, const char* location_description)
{
	const char* errorString;
	ALCenum error = alcGetError(device);

	if (error == ALC_NO_ERROR)
		return error;

	switch (error)
	{
		case ALC_INVALID_DEVICE:
			errorString = "ALC_INVALID_DEVICE: Invalid or no device selected";
			break;

		case ALC_INVALID_CONTEXT:
			errorString = "ALC_INVALID_CONTEXT: Invalid or no context selected";
			break;

		case ALC_INVALID_ENUM:
			errorString = "ALC_INVALID_ENUM: Invalid enum value";
			break;

		case ALC_INVALID_VALUE:
			errorString = "ALC_INVALID_VALUE: Invalid parameter value";
			break;

		case ALC_OUT_OF_MEMORY:
			errorString = "ALC_OUT_OF_MEMORY: OpenAL ran out of memory";
			break;

		default:
			sasprintf((char**)&errorString, "unknown error code (%d); please report this number (along with the "
			          "fact that it is an \"unknown OpenAL error code\"): 0x%x", (int)error, (unsigned int)error);
			break;
	}

	debug(LOG_SOUND, "OpenAL raised a context error: \"%s\"; at %s", errorString, location_description);

	return error;
}
コード例 #14
0
ALenum __sound_GetError(const char* location_description)
{
	const char* errorString;
	ALenum error = alGetError();

	if (error == AL_NO_ERROR)
		return error;

	switch (error)
	{
		case AL_INVALID_NAME:
			errorString = "AL_INVALID_NAME: Invalid name parameter passed";
			break;

		case AL_INVALID_ENUM:
			errorString = "AL_INVALID_ENUM: Invalid enum value";
			break;

		case AL_INVALID_VALUE:
			errorString = "AL_INVALID_VALUE: Invalid parameter value";
			break;

		case AL_INVALID_OPERATION:
			errorString = "AL_INVALID_OPERATION: Illegal call";
			break;

		case AL_OUT_OF_MEMORY:
			errorString = "AL_OUT_OF_MEMORY: OpenAL ran out of memory";
			break;

		default:
			sasprintf((char**)&errorString, "unknown error code (%d); please report this number (along with the "
			          "fact that it is an \"unknown OpenAL error code\"): 0x%x", (int)error, (unsigned int)error);
			break;
	}

	debug(LOG_SOUND, "OpenAL raised an error: \"%s\"; at %s", errorString, location_description);

	return error;
}
コード例 #15
0
ファイル: main.c プロジェクト: Acidburn0zzz/i3
int main(int argc, char *argv[]) {
    char *env_socket_path = getenv("I3SOCK");
    if (env_socket_path)
        socket_path = sstrdup(env_socket_path);
    else
        socket_path = NULL;
    int o, option_index = 0;
    uint32_t message_type = I3_IPC_MESSAGE_TYPE_COMMAND;
    char *payload = NULL;
    bool quiet = false;

    static struct option long_options[] = {
        {"socket", required_argument, 0, 's'},
        {"type", required_argument, 0, 't'},
        {"version", no_argument, 0, 'v'},
        {"quiet", no_argument, 0, 'q'},
        {"help", no_argument, 0, 'h'},
        {0, 0, 0, 0}};

    char *options_string = "s:t:vhq";

    while ((o = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) {
        if (o == 's') {
            if (socket_path != NULL)
                free(socket_path);
            socket_path = sstrdup(optarg);
        } else if (o == 't') {
            if (strcasecmp(optarg, "command") == 0)
                message_type = I3_IPC_MESSAGE_TYPE_COMMAND;
            else if (strcasecmp(optarg, "get_workspaces") == 0)
                message_type = I3_IPC_MESSAGE_TYPE_GET_WORKSPACES;
            else if (strcasecmp(optarg, "get_outputs") == 0)
                message_type = I3_IPC_MESSAGE_TYPE_GET_OUTPUTS;
            else if (strcasecmp(optarg, "get_tree") == 0)
                message_type = I3_IPC_MESSAGE_TYPE_GET_TREE;
            else if (strcasecmp(optarg, "get_marks") == 0)
                message_type = I3_IPC_MESSAGE_TYPE_GET_MARKS;
            else if (strcasecmp(optarg, "get_bar_config") == 0)
                message_type = I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG;
            else if (strcasecmp(optarg, "get_version") == 0)
                message_type = I3_IPC_MESSAGE_TYPE_GET_VERSION;
            else {
                printf("Unknown message type\n");
                printf("Known types: command, get_workspaces, get_outputs, get_tree, get_marks, get_bar_config, get_version\n");
                exit(EXIT_FAILURE);
            }
        } else if (o == 'q') {
            quiet = true;
        } else if (o == 'v') {
            printf("i3-msg " I3_VERSION "\n");
            return 0;
        } else if (o == 'h') {
            printf("i3-msg " I3_VERSION "\n");
            printf("i3-msg [-s <socket>] [-t <type>] <message>\n");
            return 0;
        }
    }

    if (socket_path == NULL)
        socket_path = root_atom_contents("I3_SOCKET_PATH", NULL, 0);

    /* Fall back to the default socket path */
    if (socket_path == NULL)
        socket_path = sstrdup("/tmp/i3-ipc.sock");

    /* Use all arguments, separated by whitespace, as payload.
     * This way, you don’t have to do i3-msg 'mark foo', you can use
     * i3-msg mark foo */
    while (optind < argc) {
        if (!payload) {
            payload = sstrdup(argv[optind]);
        } else {
            char *both;
            sasprintf(&both, "%s %s", payload, argv[optind]);
            free(payload);
            payload = both;
        }
        optind++;
    }

    if (!payload)
        payload = "";

    int sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
    if (sockfd == -1)
        err(EXIT_FAILURE, "Could not create socket");

    struct sockaddr_un addr;
    memset(&addr, 0, sizeof(struct sockaddr_un));
    addr.sun_family = AF_LOCAL;
    strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);
    if (connect(sockfd, (const struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0)
        err(EXIT_FAILURE, "Could not connect to i3 on socket \"%s\"", socket_path);

    if (ipc_send_message(sockfd, strlen(payload), message_type, (uint8_t *)payload) == -1)
        err(EXIT_FAILURE, "IPC: write()");

    if (quiet)
        return 0;

    uint32_t reply_length;
    uint32_t reply_type;
    uint8_t *reply;
    int ret;
    if ((ret = ipc_recv_message(sockfd, &reply_type, &reply_length, &reply)) != 0) {
        if (ret == -1)
            err(EXIT_FAILURE, "IPC: read()");
        exit(1);
    }
    if (reply_type != message_type)
        errx(EXIT_FAILURE, "IPC: Received reply of type %d but expected %d", reply_type, message_type);
    /* For the reply of commands, have a look if that command was successful.
     * If not, nicely format the error message. */
    if (reply_type == I3_IPC_MESSAGE_TYPE_COMMAND) {
        yajl_handle handle;
        handle = yajl_alloc(&reply_callbacks, NULL, NULL);
        yajl_status state = yajl_parse(handle, (const unsigned char *)reply, reply_length);
        switch (state) {
            case yajl_status_ok:
                break;
            case yajl_status_client_canceled:
            case yajl_status_error:
                errx(EXIT_FAILURE, "IPC: Could not parse JSON reply.");
        }

        /* NB: We still fall-through and print the reply, because even if one
         * command failed, that doesn’t mean that all commands failed. */
    }
    printf("%.*s\n", reply_length, reply);
    free(reply);

    close(sockfd);

    return 0;
}
コード例 #16
0
ファイル: outputs.c プロジェクト: fernandoataoldotcom/i3
/*
 * Parse a key.
 *
 * Essentially we just save it in the parsing state
 *
 */
static int outputs_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) {
    struct outputs_json_params *params = (struct outputs_json_params *)params_;
    FREE(params->cur_key);
    sasprintf(&(params->cur_key), "%.*s", keyLen, keyVal);
    return 1;
}
コード例 #17
0
ファイル: get_exe_path.c プロジェクト: bsmr-i3/i3
/*
 * This function returns the absolute path to the executable it is running in.
 *
 * The implementation follows http://stackoverflow.com/a/933996/712014
 *
 * Returned value must be freed by the caller.
 */
char *get_exe_path(const char *argv0) {
    size_t destpath_size = 1024;
    size_t tmp_size = 1024;
    char *destpath = smalloc(destpath_size);
    char *tmp = smalloc(tmp_size);

#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
/* Linux and Debian/kFreeBSD provide /proc/self/exe */
#if defined(__linux__) || defined(__FreeBSD_kernel__)
    const char *exepath = "/proc/self/exe";
#elif defined(__FreeBSD__)
    const char *exepath = "/proc/curproc/file";
#endif
    ssize_t linksize;

    while ((linksize = readlink(exepath, destpath, destpath_size)) == (ssize_t)destpath_size) {
        destpath_size = destpath_size * 2;
        destpath = srealloc(destpath, destpath_size);
    }
    if (linksize != -1) {
        /* readlink() does not NULL-terminate strings, so we have to. */
        destpath[linksize] = '\0';
        free(tmp);
        return destpath;
    }
#endif

    /* argv[0] is most likely a full path if it starts with a slash. */
    if (argv0[0] == '/') {
        free(tmp);
        free(destpath);
        return sstrdup(argv0);
    }

    /* if argv[0] contains a /, prepend the working directory */
    if (strchr(argv0, '/') != NULL) {
        char *retgcwd;
        while ((retgcwd = getcwd(tmp, tmp_size)) == NULL && errno == ERANGE) {
            tmp_size = tmp_size * 2;
            tmp = srealloc(tmp, tmp_size);
        }
        if (retgcwd != NULL) {
            free(destpath);
            sasprintf(&destpath, "%s/%s", tmp, argv0);
            free(tmp);
            return destpath;
        }
    }

    /* Fall back to searching $PATH (or _CS_PATH in absence of $PATH). */
    char *path = getenv("PATH");
    if (path == NULL) {
        /* _CS_PATH is typically something like "/bin:/usr/bin" */
        while (confstr(_CS_PATH, tmp, tmp_size) > tmp_size) {
            tmp_size = tmp_size * 2;
            tmp = srealloc(tmp, tmp_size);
        }
        sasprintf(&path, ":%s", tmp);
    } else {
        path = sstrdup(path);
    }
    const char *component;
    char *str = path;
    while (1) {
        if ((component = strtok(str, ":")) == NULL)
            break;
        str = NULL;
        free(destpath);
        sasprintf(&destpath, "%s/%s", component, argv0);
        /* Of course this is not 100% equivalent to actually exec()ing the
		 * binary, but meh. */
        if (access(destpath, X_OK) == 0) {
            free(path);
            free(tmp);
            return destpath;
        }
    }
    free(destpath);
    free(path);
    free(tmp);

    /* Last resort: maybe it’s in /usr/bin? */
    return sstrdup("/usr/bin/i3-nagbar");
}
コード例 #18
0
ファイル: manage.c プロジェクト: Acidburn0zzz/i3
/*
 * Do some sanity checks and then reparent the window.
 *
 */
void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cookie,
                   bool needs_to_be_mapped) {
    xcb_drawable_t d = {window};
    xcb_get_geometry_cookie_t geomc;
    xcb_get_geometry_reply_t *geom;
    xcb_get_window_attributes_reply_t *attr = NULL;

    xcb_get_property_cookie_t wm_type_cookie, strut_cookie, state_cookie,
        utf8_title_cookie, title_cookie,
        class_cookie, leader_cookie, transient_cookie,
        role_cookie, startup_id_cookie, wm_hints_cookie,
        wm_normal_hints_cookie, motif_wm_hints_cookie;

    geomc = xcb_get_geometry(conn, d);

    /* Check if the window is mapped (it could be not mapped when intializing and
       calling manage_window() for every window) */
    if ((attr = xcb_get_window_attributes_reply(conn, cookie, 0)) == NULL) {
        DLOG("Could not get attributes\n");
        xcb_discard_reply(conn, geomc.sequence);
        return;
    }

    if (needs_to_be_mapped && attr->map_state != XCB_MAP_STATE_VIEWABLE) {
        xcb_discard_reply(conn, geomc.sequence);
        goto out;
    }

    /* Don’t manage clients with the override_redirect flag */
    if (attr->override_redirect) {
        xcb_discard_reply(conn, geomc.sequence);
        goto out;
    }

    /* Check if the window is already managed */
    if (con_by_window_id(window) != NULL) {
        DLOG("already managed (by con %p)\n", con_by_window_id(window));
        xcb_discard_reply(conn, geomc.sequence);
        goto out;
    }

    /* Get the initial geometry (position, size, …) */
    if ((geom = xcb_get_geometry_reply(conn, geomc, 0)) == NULL) {
        DLOG("could not get geometry\n");
        goto out;
    }

    uint32_t values[1];

    /* Set a temporary event mask for the new window, consisting only of
     * PropertyChange and StructureNotify. We need to be notified of
     * PropertyChanges because the client can change its properties *after* we
     * requested them but *before* we actually reparented it and have set our
     * final event mask.
     * We need StructureNotify because the client may unmap the window before
     * we get to re-parent it.
     * If this request fails, we assume the client has already unmapped the
     * window between the MapRequest and our event mask change. */
    values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE |
                XCB_EVENT_MASK_STRUCTURE_NOTIFY;
    xcb_void_cookie_t event_mask_cookie =
        xcb_change_window_attributes_checked(conn, window, XCB_CW_EVENT_MASK, values);
    if (xcb_request_check(conn, event_mask_cookie) != NULL) {
        LOG("Could not change event mask, the window probably already disappeared.\n");
        goto out;
    }

#define GET_PROPERTY(atom, len) xcb_get_property(conn, false, window, atom, XCB_GET_PROPERTY_TYPE_ANY, 0, len)

    wm_type_cookie = GET_PROPERTY(A__NET_WM_WINDOW_TYPE, UINT32_MAX);
    strut_cookie = GET_PROPERTY(A__NET_WM_STRUT_PARTIAL, UINT32_MAX);
    state_cookie = GET_PROPERTY(A__NET_WM_STATE, UINT32_MAX);
    utf8_title_cookie = GET_PROPERTY(A__NET_WM_NAME, 128);
    leader_cookie = GET_PROPERTY(A_WM_CLIENT_LEADER, UINT32_MAX);
    transient_cookie = GET_PROPERTY(XCB_ATOM_WM_TRANSIENT_FOR, UINT32_MAX);
    title_cookie = GET_PROPERTY(XCB_ATOM_WM_NAME, 128);
    class_cookie = GET_PROPERTY(XCB_ATOM_WM_CLASS, 128);
    role_cookie = GET_PROPERTY(A_WM_WINDOW_ROLE, 128);
    startup_id_cookie = GET_PROPERTY(A__NET_STARTUP_ID, 512);
    wm_hints_cookie = xcb_icccm_get_wm_hints(conn, window);
    wm_normal_hints_cookie = xcb_icccm_get_wm_normal_hints(conn, window);
    motif_wm_hints_cookie = GET_PROPERTY(A__MOTIF_WM_HINTS, 5 * sizeof(uint64_t));

    DLOG("Managing window 0x%08x\n", window);

    i3Window *cwindow = scalloc(1, sizeof(i3Window));
    cwindow->id = window;
    cwindow->depth = get_visual_depth(attr->visual);

    /* We need to grab buttons 1-3 for click-to-focus and buttons 1-5
     * to allow for mouse bindings using --whole-window to work correctly. */
    xcb_grab_button(conn, false, window, XCB_EVENT_MASK_BUTTON_PRESS,
                    XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE,
                    XCB_BUTTON_INDEX_ANY,
                    XCB_BUTTON_MASK_ANY /* don’t filter for any modifiers */);

    /* update as much information as possible so far (some replies may be NULL) */
    window_update_class(cwindow, xcb_get_property_reply(conn, class_cookie, NULL), true);
    window_update_name_legacy(cwindow, xcb_get_property_reply(conn, title_cookie, NULL), true);
    window_update_name(cwindow, xcb_get_property_reply(conn, utf8_title_cookie, NULL), true);
    window_update_leader(cwindow, xcb_get_property_reply(conn, leader_cookie, NULL));
    window_update_transient_for(cwindow, xcb_get_property_reply(conn, transient_cookie, NULL));
    window_update_strut_partial(cwindow, xcb_get_property_reply(conn, strut_cookie, NULL));
    window_update_role(cwindow, xcb_get_property_reply(conn, role_cookie, NULL), true);
    bool urgency_hint;
    window_update_hints(cwindow, xcb_get_property_reply(conn, wm_hints_cookie, NULL), &urgency_hint);
    border_style_t motif_border_style = BS_NORMAL;
    window_update_motif_hints(cwindow, xcb_get_property_reply(conn, motif_wm_hints_cookie, NULL), &motif_border_style);
    xcb_size_hints_t wm_size_hints;
    if (!xcb_icccm_get_wm_size_hints_reply(conn, wm_normal_hints_cookie, &wm_size_hints, NULL))
        memset(&wm_size_hints, '\0', sizeof(xcb_size_hints_t));
    xcb_get_property_reply_t *type_reply = xcb_get_property_reply(conn, wm_type_cookie, NULL);
    xcb_get_property_reply_t *state_reply = xcb_get_property_reply(conn, state_cookie, NULL);

    xcb_get_property_reply_t *startup_id_reply;
    startup_id_reply = xcb_get_property_reply(conn, startup_id_cookie, NULL);
    char *startup_ws = startup_workspace_for_window(cwindow, startup_id_reply);
    DLOG("startup workspace = %s\n", startup_ws);

    /* check if the window needs WM_TAKE_FOCUS */
    cwindow->needs_take_focus = window_supports_protocol(cwindow->id, A_WM_TAKE_FOCUS);

    /* read the preferred _NET_WM_WINDOW_TYPE atom */
    cwindow->window_type = xcb_get_preferred_window_type(type_reply);

    /* Where to start searching for a container that swallows the new one? */
    Con *search_at = croot;

    if (xcb_reply_contains_atom(type_reply, A__NET_WM_WINDOW_TYPE_DOCK)) {
        LOG("This window is of type dock\n");
        Output *output = get_output_containing(geom->x, geom->y);
        if (output != NULL) {
            DLOG("Starting search at output %s\n", output->name);
            search_at = output->con;
        }

        /* find out the desired position of this dock window */
        if (cwindow->reserved.top > 0 && cwindow->reserved.bottom == 0) {
            DLOG("Top dock client\n");
            cwindow->dock = W_DOCK_TOP;
        } else if (cwindow->reserved.top == 0 && cwindow->reserved.bottom > 0) {
            DLOG("Bottom dock client\n");
            cwindow->dock = W_DOCK_BOTTOM;
        } else {
            DLOG("Ignoring invalid reserved edges (_NET_WM_STRUT_PARTIAL), using position as fallback:\n");
            if (geom->y < (int16_t)(search_at->rect.height / 2)) {
                DLOG("geom->y = %d < rect.height / 2 = %d, it is a top dock client\n",
                     geom->y, (search_at->rect.height / 2));
                cwindow->dock = W_DOCK_TOP;
            } else {
                DLOG("geom->y = %d >= rect.height / 2 = %d, it is a bottom dock client\n",
                     geom->y, (search_at->rect.height / 2));
                cwindow->dock = W_DOCK_BOTTOM;
            }
        }
    }

    DLOG("Initial geometry: (%d, %d, %d, %d)\n", geom->x, geom->y, geom->width, geom->height);

    Con *nc = NULL;
    Match *match = NULL;
    Assignment *assignment;

    /* TODO: two matches for one container */

    /* See if any container swallows this new window */
    nc = con_for_window(search_at, cwindow, &match);
    if (nc == NULL) {
        /* If not, check if it is assigned to a specific workspace */
        if ((assignment = assignment_for(cwindow, A_TO_WORKSPACE))) {
            DLOG("Assignment matches (%p)\n", match);
            Con *assigned_ws = workspace_get(assignment->dest.workspace, NULL);
            nc = con_descend_tiling_focused(assigned_ws);
            DLOG("focused on ws %s: %p / %s\n", assigned_ws->name, nc, nc->name);
            if (nc->type == CT_WORKSPACE)
                nc = tree_open_con(nc, cwindow);
            else
                nc = tree_open_con(nc->parent, cwindow);

            /* set the urgency hint on the window if the workspace is not visible */
            if (!workspace_is_visible(assigned_ws))
                urgency_hint = true;
        } else if (startup_ws) {
            /* If it’s not assigned, but was started on a specific workspace,
             * we want to open it there */
            DLOG("Using workspace on which this application was started (%s)\n", startup_ws);
            nc = con_descend_tiling_focused(workspace_get(startup_ws, NULL));
            DLOG("focused on ws %s: %p / %s\n", startup_ws, nc, nc->name);
            if (nc->type == CT_WORKSPACE)
                nc = tree_open_con(nc, cwindow);
            else
                nc = tree_open_con(nc->parent, cwindow);
        } else {
            /* If not, insert it at the currently focused position */
            if (focused->type == CT_CON && con_accepts_window(focused)) {
                LOG("using current container, focused = %p, focused->name = %s\n",
                    focused, focused->name);
                nc = focused;
            } else
                nc = tree_open_con(NULL, cwindow);
        }
    } else {
        /* M_BELOW inserts the new window as a child of the one which was
         * matched (e.g. dock areas) */
        if (match != NULL && match->insert_where == M_BELOW) {
            nc = tree_open_con(nc, cwindow);
        }

        /* If M_BELOW is not used, the container is replaced. This happens with
         * "swallows" criteria that are used for stored layouts, in which case
         * we need to remove that criterion, because they should only be valid
         * once. */
        if (match != NULL && match->insert_where != M_BELOW) {
            DLOG("Removing match %p from container %p\n", match, nc);
            TAILQ_REMOVE(&(nc->swallow_head), match, matches);
            match_free(match);
        }
    }

    DLOG("new container = %p\n", nc);
    if (nc->window != NULL && nc->window != cwindow) {
        if (!restore_kill_placeholder(nc->window->id)) {
            DLOG("Uh?! Container without a placeholder, but with a window, has swallowed this to-be-managed window?!\n");
        } else {
            /* Remove remaining criteria, the first swallowed window wins. */
            while (!TAILQ_EMPTY(&(nc->swallow_head))) {
                Match *first = TAILQ_FIRST(&(nc->swallow_head));
                TAILQ_REMOVE(&(nc->swallow_head), first, matches);
                match_free(first);
            }
        }
    }
    nc->window = cwindow;
    x_reinit(nc);

    nc->border_width = geom->border_width;

    char *name;
    sasprintf(&name, "[i3 con] container around %p", cwindow);
    x_set_name(nc, name);
    free(name);

    /* handle fullscreen containers */
    Con *ws = con_get_workspace(nc);
    Con *fs = (ws ? con_get_fullscreen_con(ws, CF_OUTPUT) : NULL);
    if (fs == NULL)
        fs = con_get_fullscreen_con(croot, CF_GLOBAL);

    if (xcb_reply_contains_atom(state_reply, A__NET_WM_STATE_FULLSCREEN)) {
        /* If this window is already fullscreen (after restarting!), skip
         * toggling fullscreen, that would drop it out of fullscreen mode. */
        if (fs != nc)
            con_toggle_fullscreen(nc, CF_OUTPUT);
        fs = NULL;
    }

    bool set_focus = false;

    if (fs == NULL) {
        DLOG("Not in fullscreen mode, focusing\n");
        if (!cwindow->dock) {
            /* Check that the workspace is visible and on the same output as
             * the current focused container. If the window was assigned to an
             * invisible workspace, we should not steal focus. */
            Con *current_output = con_get_output(focused);
            Con *target_output = con_get_output(ws);

            if (workspace_is_visible(ws) && current_output == target_output) {
                if (!match || !match->restart_mode) {
                    set_focus = true;
                } else
                    DLOG("not focusing, matched with restart_mode == true\n");
            } else
                DLOG("workspace not visible, not focusing\n");
        } else
            DLOG("dock, not focusing\n");
    } else {
        DLOG("fs = %p, ws = %p, not focusing\n", fs, ws);
        /* Insert the new container in focus stack *after* the currently
         * focused (fullscreen) con. This way, the new container will be
         * focused after we return from fullscreen mode */
        Con *first = TAILQ_FIRST(&(nc->parent->focus_head));
        if (first != nc) {
            /* We only modify the focus stack if the container is not already
             * the first one. This can happen when existing containers swallow
             * new windows, for example when restarting. */
            TAILQ_REMOVE(&(nc->parent->focus_head), nc, focused);
            TAILQ_INSERT_AFTER(&(nc->parent->focus_head), first, nc, focused);
        }
    }

    /* set floating if necessary */
    bool want_floating = false;
    if (xcb_reply_contains_atom(type_reply, A__NET_WM_WINDOW_TYPE_DIALOG) ||
        xcb_reply_contains_atom(type_reply, A__NET_WM_WINDOW_TYPE_UTILITY) ||
        xcb_reply_contains_atom(type_reply, A__NET_WM_WINDOW_TYPE_TOOLBAR) ||
        xcb_reply_contains_atom(type_reply, A__NET_WM_WINDOW_TYPE_SPLASH) ||
        xcb_reply_contains_atom(state_reply, A__NET_WM_STATE_MODAL) ||
        (wm_size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE &&
         wm_size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE &&
         wm_size_hints.min_height == wm_size_hints.max_height &&
         wm_size_hints.min_width == wm_size_hints.max_width)) {
        LOG("This window is a dialog window, setting floating\n");
        want_floating = true;
    }

    if (xcb_reply_contains_atom(state_reply, A__NET_WM_STATE_STICKY))
        nc->sticky = true;

    FREE(state_reply);
    FREE(type_reply);

    if (cwindow->transient_for != XCB_NONE ||
        (cwindow->leader != XCB_NONE &&
         cwindow->leader != cwindow->id &&
         con_by_window_id(cwindow->leader) != NULL)) {
        LOG("This window is transient for another window, setting floating\n");
        want_floating = true;

        if (config.popup_during_fullscreen == PDF_LEAVE_FULLSCREEN &&
            fs != NULL) {
            LOG("There is a fullscreen window, leaving fullscreen mode\n");
            con_toggle_fullscreen(fs, CF_OUTPUT);
        } else if (config.popup_during_fullscreen == PDF_SMART &&
                   fs != NULL &&
                   fs->window != NULL) {
            i3Window *transient_win = cwindow;
            while (transient_win != NULL &&
                   transient_win->transient_for != XCB_NONE) {
                if (transient_win->transient_for == fs->window->id) {
                    LOG("This floating window belongs to the fullscreen window (popup_during_fullscreen == smart)\n");
                    set_focus = true;
                    break;
                }
                Con *next_transient = con_by_window_id(transient_win->transient_for);
                if (next_transient == NULL)
                    break;
                /* Some clients (e.g. x11-ssh-askpass) actually set
                 * WM_TRANSIENT_FOR to their own window id, so break instead of
                 * looping endlessly. */
                if (transient_win == next_transient->window)
                    break;
                transient_win = next_transient->window;
            }
        }
    }

    /* dock clients cannot be floating, that makes no sense */
    if (cwindow->dock)
        want_floating = false;

    /* Plasma windows set their geometry in WM_SIZE_HINTS. */
    if ((wm_size_hints.flags & XCB_ICCCM_SIZE_HINT_US_POSITION || wm_size_hints.flags & XCB_ICCCM_SIZE_HINT_P_POSITION) &&
        (wm_size_hints.flags & XCB_ICCCM_SIZE_HINT_US_SIZE || wm_size_hints.flags & XCB_ICCCM_SIZE_HINT_P_SIZE)) {
        DLOG("We are setting geometry according to wm_size_hints x=%d y=%d w=%d h=%d\n",
             wm_size_hints.x, wm_size_hints.y, wm_size_hints.width, wm_size_hints.height);
        geom->x = wm_size_hints.x;
        geom->y = wm_size_hints.y;
        geom->width = wm_size_hints.width;
        geom->height = wm_size_hints.height;
    }

    /* Store the requested geometry. The width/height gets raised to at least
     * 75x50 when entering floating mode, which is the minimum size for a
     * window to be useful (smaller windows are usually overlays/toolbars/…
     * which are not managed by the wm anyways). We store the original geometry
     * here because it’s used for dock clients. */
    if (nc->geometry.width == 0)
        nc->geometry = (Rect){geom->x, geom->y, geom->width, geom->height};

    if (motif_border_style != BS_NORMAL) {
        DLOG("MOTIF_WM_HINTS specifies decorations (border_style = %d)\n", motif_border_style);
        if (want_floating) {
            con_set_border_style(nc, motif_border_style, config.default_floating_border_width);
        } else {
            con_set_border_style(nc, motif_border_style, config.default_border_width);
        }
    }

    if (want_floating) {
        DLOG("geometry = %d x %d\n", nc->geometry.width, nc->geometry.height);
        /* automatically set the border to the default value if a motif border
         * was not specified */
        bool automatic_border = (motif_border_style == BS_NORMAL);

        floating_enable(nc, automatic_border);
    }

    /* explicitly set the border width to the default */
    if (nc->current_border_width == -1) {
        nc->current_border_width = (want_floating ? config.default_floating_border_width : config.default_border_width);
    }

    /* to avoid getting an UnmapNotify event due to reparenting, we temporarily
     * declare no interest in any state change event of this window */
    values[0] = XCB_NONE;
    xcb_change_window_attributes(conn, window, XCB_CW_EVENT_MASK, values);

    xcb_void_cookie_t rcookie = xcb_reparent_window_checked(conn, window, nc->frame, 0, 0);
    if (xcb_request_check(conn, rcookie) != NULL) {
        LOG("Could not reparent the window, aborting\n");
        goto geom_out;
    }

    values[0] = CHILD_EVENT_MASK & ~XCB_EVENT_MASK_ENTER_WINDOW;
    xcb_change_window_attributes(conn, window, XCB_CW_EVENT_MASK, values);
    xcb_flush(conn);

    /* Put the client inside the save set. Upon termination (whether killed or
     * normal exit does not matter) of the window manager, these clients will
     * be correctly reparented to their most closest living ancestor (=
     * cleanup) */
    xcb_change_save_set(conn, XCB_SET_MODE_INSERT, window);

    /* Check if any assignments match */
    run_assignments(cwindow);

    /* 'ws' may be invalid because of the assignments, e.g. when the user uses
     * "move window to workspace 1", but had it assigned to workspace 2. */
    ws = con_get_workspace(nc);

    /* If this window was put onto an invisible workspace (via assignments), we
     * render this workspace. It wouldn’t be rendered in our normal code path
     * because only the visible workspaces get rendered.
     *
     * By rendering the workspace, we assign proper coordinates (read: not
     * width=0, height=0) to the window, which is important for windows who
     * actually use them to position their GUI elements, e.g. rhythmbox. */
    if (ws && !workspace_is_visible(ws)) {
        /* This is a bit hackish: we need to copy the content container’s rect
         * to the workspace, because calling render_con() on the content
         * container would also take the shortcut and not render the invisible
         * workspace at all. However, just calling render_con() on the
         * workspace isn’t enough either — it needs the rect. */
        ws->rect = ws->parent->rect;
        render_con(ws, true);
        /* Disable setting focus, otherwise we’d move focus to an invisible
         * workspace, which we generally prevent (e.g. in
         * con_move_to_workspace). */
        set_focus = false;
    }
    render_con(croot, false);

    /* Send an event about window creation */
    ipc_send_window_event("new", nc);

    if (set_focus && assignment_for(cwindow, A_NO_FOCUS) != NULL) {
        /* The first window on a workspace should always be focused. We have to
         * compare with == 1 because the container has already been inserted at
         * this point. */
        if (con_num_children(ws) == 1) {
            DLOG("This is the first window on this workspace, ignoring no_focus.\n");
        } else {
            DLOG("no_focus was set for con = %p, not setting focus.\n", nc);
            set_focus = false;
        }
    }

    /* Defer setting focus after the 'new' event has been sent to ensure the
     * proper window event sequence. */
    if (set_focus && !nc->window->doesnt_accept_focus && nc->mapped) {
        DLOG("Now setting focus.\n");
        con_focus(nc);
    }

    tree_render();

    /* Windows might get managed with the urgency hint already set (Pidgin is
     * known to do that), so check for that and handle the hint accordingly.
     * This code needs to be in this part of manage_window() because the window
     * needs to be on the final workspace first. */
    con_set_urgency(nc, urgency_hint);

geom_out:
    free(geom);
out:
    free(attr);
    return;
}
コード例 #19
0
ファイル: manage.c プロジェクト: Eelis/i3
/*
 * Do some sanity checks and then reparent the window.
 *
 */
void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cookie,
                   bool needs_to_be_mapped) {
    xcb_drawable_t d = { window };
    xcb_get_geometry_cookie_t geomc;
    xcb_get_geometry_reply_t *geom;
    xcb_get_window_attributes_reply_t *attr = NULL;

    xcb_get_property_cookie_t wm_type_cookie, strut_cookie, state_cookie,
                              utf8_title_cookie, title_cookie,
                              class_cookie, leader_cookie, transient_cookie,
                              role_cookie, startup_id_cookie, wm_hints_cookie;
#ifdef USE_ICONS                              
    xcb_get_property_cookie_t wm_icon_cookie;
#endif

    geomc = xcb_get_geometry(conn, d);
#define FREE_GEOMETRY() do { \
    if ((geom = xcb_get_geometry_reply(conn, geomc, 0)) != NULL) \
        free(geom); \
} while (0)

    /* Check if the window is mapped (it could be not mapped when intializing and
       calling manage_window() for every window) */
    if ((attr = xcb_get_window_attributes_reply(conn, cookie, 0)) == NULL) {
        DLOG("Could not get attributes\n");
        FREE_GEOMETRY();
        return;
    }

    if (needs_to_be_mapped && attr->map_state != XCB_MAP_STATE_VIEWABLE) {
        FREE_GEOMETRY();
        goto out;
    }

    /* Don’t manage clients with the override_redirect flag */
    if (attr->override_redirect) {
        FREE_GEOMETRY();
        goto out;
    }

    /* Check if the window is already managed */
    if (con_by_window_id(window) != NULL) {
        DLOG("already managed (by con %p)\n", con_by_window_id(window));
        FREE_GEOMETRY();
        goto out;
    }

    /* Get the initial geometry (position, size, …) */
    if ((geom = xcb_get_geometry_reply(conn, geomc, 0)) == NULL) {
        DLOG("could not get geometry\n");
        goto out;
    }

    uint32_t values[1];

    /* Set a temporary event mask for the new window, consisting only of
     * PropertyChange and StructureNotify. We need to be notified of
     * PropertyChanges because the client can change its properties *after* we
     * requested them but *before* we actually reparented it and have set our
     * final event mask.
     * We need StructureNotify because the client may unmap the window before
     * we get to re-parent it.
     * If this request fails, we assume the client has already unmapped the
     * window between the MapRequest and our event mask change. */
    values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE |
                XCB_EVENT_MASK_STRUCTURE_NOTIFY;
    xcb_void_cookie_t event_mask_cookie =
        xcb_change_window_attributes_checked(conn, window, XCB_CW_EVENT_MASK, values);
    if (xcb_request_check(conn, event_mask_cookie) != NULL) {
        LOG("Could not change event mask, the window probably already disappeared.\n");
        goto out;
    }

#define GET_PROPERTY(atom, len) xcb_get_property(conn, false, window, atom, XCB_GET_PROPERTY_TYPE_ANY, 0, len)

    wm_type_cookie = GET_PROPERTY(A__NET_WM_WINDOW_TYPE, UINT32_MAX);
    strut_cookie = GET_PROPERTY(A__NET_WM_STRUT_PARTIAL, UINT32_MAX);
    state_cookie = GET_PROPERTY(A__NET_WM_STATE, UINT32_MAX);
    utf8_title_cookie = GET_PROPERTY(A__NET_WM_NAME, 128);
    leader_cookie = GET_PROPERTY(A_WM_CLIENT_LEADER, UINT32_MAX);
    transient_cookie = GET_PROPERTY(XCB_ATOM_WM_TRANSIENT_FOR, UINT32_MAX);
    title_cookie = GET_PROPERTY(XCB_ATOM_WM_NAME, 128);
    class_cookie = GET_PROPERTY(XCB_ATOM_WM_CLASS, 128);
    role_cookie = GET_PROPERTY(A_WM_WINDOW_ROLE, 128);
    startup_id_cookie = GET_PROPERTY(A__NET_STARTUP_ID, 512);
    wm_hints_cookie = xcb_icccm_get_wm_hints(conn, window);
#ifdef USE_ICONS
    wm_icon_cookie = xcb_get_property_unchecked(conn, false, window, A__NET_WM_ICON, XCB_ATOM_CARDINAL, 0, UINT32_MAX);
#endif
    /* TODO: also get wm_normal_hints here. implement after we got rid of xcb-event */

    DLOG("Managing window 0x%08x\n", window);

    i3Window *cwindow = scalloc(sizeof(i3Window));
    cwindow->id = window;
    cwindow->depth = get_visual_depth(attr->visual);

    /* We need to grab the mouse buttons for click to focus */
    xcb_grab_button(conn, false, window, XCB_EVENT_MASK_BUTTON_PRESS,
                    XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE,
                    1 /* left mouse button */,
                    XCB_BUTTON_MASK_ANY /* don’t filter for any modifiers */);

    xcb_grab_button(conn, false, window, XCB_EVENT_MASK_BUTTON_PRESS,
                    XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE,
                    2 /* middle mouse button */,
                    XCB_BUTTON_MASK_ANY /* don’t filter for any modifiers */);

    xcb_grab_button(conn, false, window, XCB_EVENT_MASK_BUTTON_PRESS,
                    XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE,
                    3 /* right mouse button */,
                    XCB_BUTTON_MASK_ANY /* don’t filter for any modifiers */);

    /* update as much information as possible so far (some replies may be NULL) */
    window_update_class(cwindow, xcb_get_property_reply(conn, class_cookie, NULL), true);
    window_update_name_legacy(cwindow, xcb_get_property_reply(conn, title_cookie, NULL), true);
    window_update_name(cwindow, xcb_get_property_reply(conn, utf8_title_cookie, NULL), true);
    window_update_leader(cwindow, xcb_get_property_reply(conn, leader_cookie, NULL));
    window_update_transient_for(cwindow, xcb_get_property_reply(conn, transient_cookie, NULL));
    window_update_strut_partial(cwindow, xcb_get_property_reply(conn, strut_cookie, NULL));
    window_update_role(cwindow, xcb_get_property_reply(conn, role_cookie, NULL), true);
    window_update_hints(cwindow, xcb_get_property_reply(conn, wm_hints_cookie, NULL));
#ifdef USE_ICONS
    window_update_icon(cwindow, xcb_get_property_reply(conn, wm_icon_cookie, NULL));
#endif

    xcb_get_property_reply_t *startup_id_reply;
    startup_id_reply = xcb_get_property_reply(conn, startup_id_cookie, NULL);
    char *startup_ws = startup_workspace_for_window(cwindow, startup_id_reply);
    DLOG("startup workspace = %s\n", startup_ws);

    /* check if the window needs WM_TAKE_FOCUS */
    cwindow->needs_take_focus = window_supports_protocol(cwindow->id, A_WM_TAKE_FOCUS);

    /* Where to start searching for a container that swallows the new one? */
    Con *search_at = croot;

    xcb_get_property_reply_t *reply = xcb_get_property_reply(conn, wm_type_cookie, NULL);
    if (xcb_reply_contains_atom(reply, A__NET_WM_WINDOW_TYPE_DOCK)) {
        LOG("This window is of type dock\n");
        Output *output = get_output_containing(geom->x, geom->y);
        if (output != NULL) {
            DLOG("Starting search at output %s\n", output->name);
            search_at = output->con;
        }

        /* find out the desired position of this dock window */
        if (cwindow->reserved.top > 0 && cwindow->reserved.bottom == 0) {
            DLOG("Top dock client\n");
            cwindow->dock = W_DOCK_TOP;
        } else if (cwindow->reserved.top == 0 && cwindow->reserved.bottom > 0) {
            DLOG("Bottom dock client\n");
            cwindow->dock = W_DOCK_BOTTOM;
        } else {
            DLOG("Ignoring invalid reserved edges (_NET_WM_STRUT_PARTIAL), using position as fallback:\n");
            if (geom->y < (search_at->rect.height / 2)) {
                DLOG("geom->y = %d < rect.height / 2 = %d, it is a top dock client\n",
                     geom->y, (search_at->rect.height / 2));
                cwindow->dock = W_DOCK_TOP;
            } else {
                DLOG("geom->y = %d >= rect.height / 2 = %d, it is a bottom dock client\n",
                     geom->y, (search_at->rect.height / 2));
                cwindow->dock = W_DOCK_BOTTOM;
            }
        }
    }

    DLOG("Initial geometry: (%d, %d, %d, %d)\n", geom->x, geom->y, geom->width, geom->height);

    Con *nc = NULL;
    Match *match = NULL;
    Assignment *assignment;

    /* TODO: two matches for one container */

    /* See if any container swallows this new window */
    nc = con_for_window(search_at, cwindow, &match);
    if (nc == NULL) {
        /* If not, check if it is assigned to a specific workspace / output */
        if ((assignment = assignment_for(cwindow, A_TO_WORKSPACE | A_TO_OUTPUT))) {
            DLOG("Assignment matches (%p)\n", match);
            if (assignment->type == A_TO_WORKSPACE) {
                nc = con_descend_tiling_focused(workspace_get(assignment->dest.workspace, NULL));
                DLOG("focused on ws %s: %p / %s\n", assignment->dest.workspace, nc, nc->name);
                if (nc->type == CT_WORKSPACE)
                    nc = tree_open_con(nc, cwindow);
                else nc = tree_open_con(nc->parent, cwindow);
            }
        /* TODO: handle assignments with type == A_TO_OUTPUT */
        } else if (startup_ws) {
            /* If it’s not assigned, but was started on a specific workspace,
             * we want to open it there */
            DLOG("Using workspace on which this application was started (%s)\n", startup_ws);
            nc = con_descend_tiling_focused(workspace_get(startup_ws, NULL));
            DLOG("focused on ws %s: %p / %s\n", startup_ws, nc, nc->name);
            if (nc->type == CT_WORKSPACE)
                nc = tree_open_con(nc, cwindow);
            else nc = tree_open_con(nc->parent, cwindow);
        } else {
            /* If not, insert it at the currently focused position */
            if (focused->type == CT_CON && con_accepts_window(focused)) {
                LOG("using current container, focused = %p, focused->name = %s\n",
                                focused, focused->name);
                nc = focused;
            } else nc = tree_open_con(NULL, cwindow);
        }
    } else {
        /* M_BELOW inserts the new window as a child of the one which was
         * matched (e.g. dock areas) */
        if (match != NULL && match->insert_where == M_BELOW) {
            nc = tree_open_con(nc, cwindow);
        }
    }

    DLOG("new container = %p\n", nc);
    nc->window = cwindow;
    x_reinit(nc);

    nc->border_width = geom->border_width;

    char *name;
    sasprintf(&name, "[i3 con] container around %p", cwindow);
    x_set_name(nc, name);
    free(name);

    Con *ws = con_get_workspace(nc);
    Con *fs = (ws ? con_get_fullscreen_con(ws, CF_OUTPUT) : NULL);
    if (fs == NULL)
        fs = con_get_fullscreen_con(croot, CF_GLOBAL);

    if (fs == NULL) {
        DLOG("Not in fullscreen mode, focusing\n");
        if (!cwindow->dock) {
            /* Check that the workspace is visible and on the same output as
             * the current focused container. If the window was assigned to an
             * invisible workspace, we should not steal focus. */
            Con *current_output = con_get_output(focused);
            Con *target_output = con_get_output(ws);

            if (workspace_is_visible(ws) && current_output == target_output) {
                if (!match || !match->restart_mode) {
                    con_focus(nc);
                } else DLOG("not focusing, matched with restart_mode == true\n");
            } else DLOG("workspace not visible, not focusing\n");
        } else DLOG("dock, not focusing\n");
    } else {
        DLOG("fs = %p, ws = %p, not focusing\n", fs, ws);
        /* Insert the new container in focus stack *after* the currently
         * focused (fullscreen) con. This way, the new container will be
         * focused after we return from fullscreen mode */
        Con *first = TAILQ_FIRST(&(nc->parent->focus_head));
        if (first != nc) {
            /* We only modify the focus stack if the container is not already
             * the first one. This can happen when existing containers swallow
             * new windows, for example when restarting. */
            TAILQ_REMOVE(&(nc->parent->focus_head), nc, focused);
            TAILQ_INSERT_AFTER(&(nc->parent->focus_head), first, nc, focused);
        }
    }

    /* set floating if necessary */
    bool want_floating = false;
    if (xcb_reply_contains_atom(reply, A__NET_WM_WINDOW_TYPE_DIALOG) ||
        xcb_reply_contains_atom(reply, A__NET_WM_WINDOW_TYPE_UTILITY) ||
        xcb_reply_contains_atom(reply, A__NET_WM_WINDOW_TYPE_TOOLBAR) ||
        xcb_reply_contains_atom(reply, A__NET_WM_WINDOW_TYPE_SPLASH)) {
        LOG("This window is a dialog window, setting floating\n");
        want_floating = true;
    }

    FREE(reply);

    if (cwindow->transient_for != XCB_NONE ||
        (cwindow->leader != XCB_NONE &&
         cwindow->leader != cwindow->id &&
         con_by_window_id(cwindow->leader) != NULL)) {
        LOG("This window is transient for another window, setting floating\n");
        want_floating = true;

        if (config.popup_during_fullscreen == PDF_LEAVE_FULLSCREEN &&
            fs != NULL) {
            LOG("There is a fullscreen window, leaving fullscreen mode\n");
            con_toggle_fullscreen(fs, CF_OUTPUT);
        } else if (config.popup_during_fullscreen == PDF_SMART &&
                   fs != NULL &&
                   fs->window != NULL) {
            i3Window *transient_win = cwindow;
            while (transient_win != NULL &&
                   transient_win->transient_for != XCB_NONE) {
                if (transient_win->transient_for == fs->window->id) {
                    LOG("This floating window belongs to the fullscreen window (popup_during_fullscreen == smart)\n");
                    con_focus(nc);
                    break;
                }
                Con *next_transient = con_by_window_id(transient_win->transient_for);
                if (next_transient == NULL)
                    break;
                transient_win = next_transient->window;
            }
        }
    }

    /* dock clients cannot be floating, that makes no sense */
    if (cwindow->dock)
        want_floating = false;

    /* Store the requested geometry. The width/height gets raised to at least
     * 75x50 when entering floating mode, which is the minimum size for a
     * window to be useful (smaller windows are usually overlays/toolbars/…
     * which are not managed by the wm anyways). We store the original geometry
     * here because it’s used for dock clients. */
    nc->geometry = (Rect){ geom->x, geom->y, geom->width, geom->height };

    if (want_floating) {
        DLOG("geometry = %d x %d\n", nc->geometry.width, nc->geometry.height);
        floating_enable(nc, true);
    }

    /* to avoid getting an UnmapNotify event due to reparenting, we temporarily
     * declare no interest in any state change event of this window */
    values[0] = XCB_NONE;
    xcb_change_window_attributes(conn, window, XCB_CW_EVENT_MASK, values);

    xcb_void_cookie_t rcookie = xcb_reparent_window_checked(conn, window, nc->frame, 0, 0);
    if (xcb_request_check(conn, rcookie) != NULL) {
        LOG("Could not reparent the window, aborting\n");
        goto geom_out;
    }

    values[0] = CHILD_EVENT_MASK & ~XCB_EVENT_MASK_ENTER_WINDOW;
    xcb_change_window_attributes(conn, window, XCB_CW_EVENT_MASK, values);
    xcb_flush(conn);

    reply = xcb_get_property_reply(conn, state_cookie, NULL);
    if (xcb_reply_contains_atom(reply, A__NET_WM_STATE_FULLSCREEN))
        con_toggle_fullscreen(nc, CF_OUTPUT);

    FREE(reply);

    /* Put the client inside the save set. Upon termination (whether killed or
     * normal exit does not matter) of the window manager, these clients will
     * be correctly reparented to their most closest living ancestor (=
     * cleanup) */
    xcb_change_save_set(conn, XCB_SET_MODE_INSERT, window);

    /* Check if any assignments match */
    run_assignments(cwindow);

    /* 'ws' may be invalid because of the assignments, e.g. when the user uses
     * "move window to workspace 1", but had it assigned to workspace 2. */
    ws = con_get_workspace(nc);

    /* If this window was put onto an invisible workspace (via assignments), we
     * render this workspace. It wouldn’t be rendered in our normal code path
     * because only the visible workspaces get rendered.
     *
     * By rendering the workspace, we assign proper coordinates (read: not
     * width=0, height=0) to the window, which is important for windows who
     * actually use them to position their GUI elements, e.g. rhythmbox. */
    if (ws && !workspace_is_visible(ws)) {
        /* This is a bit hackish: we need to copy the content container’s rect
         * to the workspace, because calling render_con() on the content
         * container would also take the shortcut and not render the invisible
         * workspace at all. However, just calling render_con() on the
         * workspace isn’t enough either — it needs the rect. */
        ws->rect = ws->parent->rect;
        render_con(ws, true);
    }
    tree_render();

    /* Send an event about window creation */
    ipc_send_window_new_event(nc);

geom_out:
    free(geom);
out:
    free(attr);
    return;
}
コード例 #20
0
ファイル: inject_randr1.5.c プロジェクト: bsmr-i3/i3
int main(int argc, char *argv[]) {
    static struct option long_options[] = {
        {"getmonitors_reply", required_argument, 0, 0},
        {0, 0, 0, 0},
    };
    char *options_string = "";
    int opt;
    int option_index = 0;

    while ((opt = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) {
        switch (opt) {
            case 0:
                if (strcmp(long_options[option_index].name, "getmonitors_reply") == 0) {
                    must_read_reply(optarg);
                }
                break;
            default:
                exit(EXIT_FAILURE);
        }
    }

    if (optind >= argc) {
        errx(EXIT_FAILURE, "syntax: %s [options] <command>\n", argv[0]);
    }

    int fd = socket(AF_LOCAL, SOCK_STREAM, 0);
    if (fd == -1) {
        err(EXIT_FAILURE, "socket(AF_UNIX)");
    }

    if (fcntl(fd, F_SETFD, FD_CLOEXEC)) {
        warn("Could not set FD_CLOEXEC");
    }

    struct sockaddr_un addr;
    memset(&addr, 0, sizeof(struct sockaddr_un));
    addr.sun_family = AF_UNIX;
    int i;
    bool bound = false;
    for (i = 0; i < 100; i++) {
        /* XXX: The path to X11 sockets differs on some platforms (e.g. Trusted
         * Solaris, HPUX), but since libxcb doesn’t provide a function to
         * generate the path, we’ll just have to hard-code it for now. */
        snprintf(addr.sun_path, sizeof(addr.sun_path), "/tmp/.X11-unix/X%d", i);

        if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) == -1) {
            warn("bind(%s)", addr.sun_path);
        } else {
            bound = true;
            /* Let the user know bind() was successful, so that they know the
             * error messages can be disregarded. */
            fprintf(stderr, "Successfuly bound to %s\n", addr.sun_path);
            sun_path = sstrdup(addr.sun_path);
            break;
        }
    }

    if (!bound) {
        err(EXIT_FAILURE, "bind()");
    }

    atexit(cleanup_socket);

    /* This program will be started for each testcase which requires it, so we
     * expect precisely one connection. */
    if (listen(fd, 1) == -1) {
        err(EXIT_FAILURE, "listen()");
    }

    pid_t child = fork();
    if (child == -1) {
        err(EXIT_FAILURE, "fork()");
    }
    if (child == 0) {
        char *display;
        sasprintf(&display, ":%d", i);
        setenv("DISPLAY", display, 1);
        free(display);

        char **child_args = argv + optind;
        execvp(child_args[0], child_args);
        err(EXIT_FAILURE, "exec()");
    }

    struct ev_loop *loop = ev_default_loop(0);

    ev_child cw;
    ev_child_init(&cw, child_cb, child, 0);
    ev_child_start(loop, &cw);

    ev_io watcher;
    ev_io_init(&watcher, uds_connection_cb, fd, EV_READ);
    ev_io_start(loop, &watcher);

    ev_run(loop, 0);
}
コード例 #21
0
ファイル: loadsave.c プロジェクト: cybersphinx/wzgraphicsmods
//****************************************************************************************
// Load menu/save menu?
//*****************************************************************************************
static BOOL _addLoadSave(BOOL bLoad, const char *sSearchPath, const char *sExtension, const char *title)
{
	W_FORMINIT		sFormInit;
	W_BUTINIT		sButInit;
	W_LABINIT		sLabInit;
	UDWORD			slotCount;
// removed hardcoded values!  change with the defines above! -Q
	static char	sSlotCaps[totalslots][totalslotspace];
	static char	sSlotTips[totalslots][totalslotspace];
	char **i, **files;
	const char* checkExtension;

	mode = bLoad;
	debug(LOG_SAVE, "called (%d, %s, %s, %s)", bLoad, sSearchPath, sExtension, title);

	if ((bLoadSaveMode == LOAD_INGAME) || (bLoadSaveMode == SAVE_INGAME))
	{
		if (!bMultiPlayer || (NetPlay.bComms ==0))
		{
			gameTimeStop();
			if(GetGameMode() == GS_NORMAL)
			{
				BOOL radOnScreen = radarOnScreen;				// Only do this in main game.

				bRender3DOnly = true;
				radarOnScreen = false;

				displayWorld();									// Just display the 3d, no interface

				pie_UploadDisplayBuffer();			// Upload the current display back buffer into system memory.

				radarOnScreen = radOnScreen;
				bRender3DOnly = false;
			}

			setGamePauseStatus( true );
			setGameUpdatePause(true);
			setScriptPause(true);
			setScrollPause(true);
			setConsolePause(true);

		}

		forceHidePowerBar();
		intRemoveReticule();
	}

	(void) PHYSFS_mkdir(sSearchPath); // just in case

	psRequestScreen = widgCreateScreen(); // init the screen
	widgSetTipFont(psRequestScreen,font_regular);

	/* add a form to place the tabbed form on */
	memset(&sFormInit, 0, sizeof(W_FORMINIT));
	sFormInit.formID = 0;				//this adds the blue background, and the "box" behind the buttons -Q
	sFormInit.id = LOADSAVE_FORM;
	sFormInit.style = WFORM_PLAIN;
	sFormInit.x = (SWORD) LOADSAVE_X;
	sFormInit.y = (SWORD) LOADSAVE_Y;
	sFormInit.width = LOADSAVE_W;
	// we need the form to be long enough for all resolutions, so we take the total number of items * height
	// and * the gaps, add the banner, and finally, the fudge factor ;)
	sFormInit.height = (slotsInColumn * LOADENTRY_H + LOADSAVE_HGAP* slotsInColumn)+ LOADSAVE_BANNER_DEPTH+20;
	sFormInit.disableChildren = true;
	sFormInit.pDisplay = intOpenPlainForm;
	widgAddForm(psRequestScreen, &sFormInit);

	// Add Banner
	sFormInit.formID = LOADSAVE_FORM;
	sFormInit.id = LOADSAVE_BANNER;
	sFormInit.x = LOADSAVE_HGAP;
	sFormInit.y = LOADSAVE_VGAP;
	sFormInit.width = LOADSAVE_W-(2*LOADSAVE_HGAP);
	sFormInit.height = LOADSAVE_BANNER_DEPTH;
	sFormInit.disableChildren = false;
	sFormInit.pDisplay = displayLoadBanner;
	sFormInit.UserData = bLoad;
	widgAddForm(psRequestScreen, &sFormInit);


	// Add Banner Label
	memset(&sLabInit, 0, sizeof(W_LABINIT));
	sLabInit.formID = LOADSAVE_BANNER;
	sLabInit.id		= LOADSAVE_LABEL;
	sLabInit.style	= WLAB_ALIGNCENTRE;
	sLabInit.x		= 0;
	sLabInit.y		= 3;
	sLabInit.width	= LOADSAVE_W-(2*LOADSAVE_HGAP);	//LOADSAVE_W;
	sLabInit.height = LOADSAVE_BANNER_DEPTH;		//This looks right -Q
	sLabInit.pText	= title;
	sLabInit.FontID = font_regular;
	widgAddLabel(psRequestScreen, &sLabInit);


	// add cancel.
	memset(&sButInit, 0, sizeof(W_BUTINIT));
	sButInit.formID = LOADSAVE_BANNER;
	sButInit.x = 8;
	sButInit.y = 8;
	sButInit.width		= iV_GetImageWidth(IntImages,IMAGE_NRUTER);
	sButInit.height		= iV_GetImageHeight(IntImages,IMAGE_NRUTER);
	sButInit.UserData	= PACKDWORD_TRI(0,IMAGE_NRUTER , IMAGE_NRUTER);

	sButInit.id = LOADSAVE_CANCEL;
	sButInit.style = WBUT_PLAIN;
	sButInit.pTip = _("Close");
	sButInit.FontID = font_regular;
	sButInit.pDisplay = intDisplayImageHilight;
	widgAddButton(psRequestScreen, &sButInit);

	// add slots
	memset(&sButInit, 0, sizeof(W_BUTINIT));
	sButInit.formID		= LOADSAVE_FORM;
	sButInit.style		= WBUT_PLAIN;
	sButInit.width		= LOADENTRY_W;
	sButInit.height		= LOADENTRY_H;
	sButInit.pDisplay	= displayLoadSlot;
	sButInit.FontID		= font_regular;

	for(slotCount = 0; slotCount< totalslots; slotCount++)
	{
		sButInit.id		= slotCount+LOADENTRY_START;

		if(slotCount < slotsInColumn)
		{
			sButInit.x	= 22 + LOADSAVE_HGAP;
			sButInit.y	= (SWORD)((LOADSAVE_BANNER_DEPTH +(2*LOADSAVE_VGAP)) + (
				slotCount*(LOADSAVE_VGAP+LOADENTRY_H)));
		}
		else if (slotCount >= slotsInColumn && (slotCount < (slotsInColumn *2)))
		{
			sButInit.x	= 22 + (2*LOADSAVE_HGAP + LOADENTRY_W);
			sButInit.y	= (SWORD)((LOADSAVE_BANNER_DEPTH +(2* LOADSAVE_VGAP)) + (
				(slotCount % slotsInColumn)*(LOADSAVE_VGAP+LOADENTRY_H)));
		}
		else
		{
			sButInit.x	= 22 + (3*LOADSAVE_HGAP + (2*LOADENTRY_W));
			sButInit.y	= (SWORD)((LOADSAVE_BANNER_DEPTH +(2* LOADSAVE_VGAP)) + (
				(slotCount % slotsInColumn)*(LOADSAVE_VGAP+LOADENTRY_H)));
		}
		widgAddButton(psRequestScreen, &sButInit);
	}

	// fill slots.
	slotCount = 0;

	sstrcpy(sPath, sSearchPath);  // setup locals.
	sstrcpy(sExt, sExtension);

	debug(LOG_SAVE, "Searching \"%s\" for savegames", sSearchPath);

	// Check for an extension like ".ext", not "ext"
	sasprintf((char**)&checkExtension, ".%s", sExtension);

	// add savegame filenames minus extensions to buttons
	files = PHYSFS_enumerateFiles(sSearchPath);
	for (i = files; *i != NULL; ++i)
	{
		W_BUTTON *button;
		char savefile[256];
		time_t savetime;

		// See if this filename contains the extension we're looking for
		if (!strstr(*i, checkExtension))
		{
			// If it doesn't, move on to the next filename
			continue;
		}

		button = (W_BUTTON*)widgGetFromID(psRequestScreen, LOADENTRY_START + slotCount);

		debug(LOG_SAVE, "We found [%s]", *i);

		/* Figure save-time */
		snprintf(savefile, sizeof(savefile), "%s/%s", sSearchPath, *i);
		savetime = PHYSFS_getLastModTime(savefile);
		sstrcpy(sSlotTips[slotCount], ctime(&savetime));

		/* Set the button-text */
		(*i)[strlen(*i) - 4] = '\0'; // remove .gam extension
		sstrcpy(sSlotCaps[slotCount], *i);  //store it!
		
		/* Add button */
		button->pTip = sSlotTips[slotCount];
		button->pText = sSlotCaps[slotCount];
		slotCount++;		// goto next but...
		if (slotCount == totalslots)
		{
			break;
		}
	}
	PHYSFS_freeList(files);

	bLoadSaveUp = true;
	return true;
}
コード例 #22
0
ファイル: main.c プロジェクト: stfnm/i3
/*
 * Creates the config file and tells i3 to reload.
 *
 */
static void finish() {
    printf("creating \"%s\"...\n", config_path);

    if (!(dpy = XOpenDisplay(NULL)))
        errx(1, "Could not connect to X11");

    FILE *kc_config = fopen(SYSCONFDIR "/i3/config.keycodes", "r");
    if (kc_config == NULL)
        err(1, "Could not open input file \"%s\"", SYSCONFDIR "/i3/config.keycodes");

    FILE *ks_config = fopen(config_path, "w");
    if (ks_config == NULL)
        err(1, "Could not open output config file \"%s\"", config_path);
    free(config_path);

    char *line = NULL;
    size_t len = 0;
#ifndef USE_FGETLN
    ssize_t read;
#endif
    bool head_of_file = true;

    /* write a header about auto-generation to the output file */
    fputs("# This file has been auto-generated by i3-config-wizard(1).\n", ks_config);
    fputs("# It will not be overwritten, so edit it as you like.\n", ks_config);
    fputs("#\n", ks_config);
    fputs("# Should you change your keyboard layout somewhen, delete\n", ks_config);
    fputs("# this file and re-run i3-config-wizard(1).\n", ks_config);
    fputs("#\n", ks_config);

#ifdef USE_FGETLN
    char *buf = NULL;
    while ((buf = fgetln(kc_config, &len)) != NULL) {
        /* fgetln does not return null-terminated strings */
        FREE(line);
        sasprintf(&line, "%.*s", len, buf);
#else
    size_t linecap = 0;
    while ((read = getline(&line, &linecap, kc_config)) != -1) {
        len = strlen(line);
#endif
        /* skip the warning block at the beginning of the input file */
        if (head_of_file &&
            strncmp("# WARNING", line, strlen("# WARNING")) == 0)
            continue;

        head_of_file = false;

        /* Skip leading whitespace */
        char *walk = line;
        while (isspace(*walk) && walk < (line + len)) {
            /* Pre-output the skipped whitespaces to keep proper indentation */
            fputc(*walk, ks_config);
            walk++;
        }

        /* Set the modifier the user chose */
        if (strncmp(walk, "set $mod ", strlen("set $mod ")) == 0) {
            if (modifier == MOD_Mod1)
                fputs("set $mod Mod1\n", ks_config);
            else fputs("set $mod Mod4\n", ks_config);
            continue;
        }

        /* Check for 'bindcode'. If it’s not a bindcode line, we
         * just copy it to the output file */
        if (strncmp(walk, "bindcode", strlen("bindcode")) != 0) {
            fputs(walk, ks_config);
            continue;
        }
        char *result = rewrite_binding(walk);
        fputs(result, ks_config);
        free(result);
    }

    /* sync to do our best in order to have the file really stored on disk */
    fflush(ks_config);
    fsync(fileno(ks_config));

#ifndef USE_FGETLN
    free(line);
#endif

    fclose(kc_config);
    fclose(ks_config);

    /* tell i3 to reload the config file */
    int sockfd = ipc_connect(socket_path);
    ipc_send_message(sockfd, strlen("reload"), 0, (uint8_t*)"reload");
    close(sockfd);

    exit(0);
}

int main(int argc, char *argv[]) {
    config_path = resolve_tilde("~/.i3/config");
    socket_path = getenv("I3SOCK");
    char *pattern = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1";
    char *patternbold = "-misc-fixed-bold-r-normal--13-120-75-75-C-70-iso10646-1";
    int o, option_index = 0;

    static struct option long_options[] = {
        {"socket", required_argument, 0, 's'},
        {"version", no_argument, 0, 'v'},
        {"limit", required_argument, 0, 'l'},
        {"prompt", required_argument, 0, 'P'},
        {"prefix", required_argument, 0, 'p'},
        {"font", required_argument, 0, 'f'},
        {"help", no_argument, 0, 'h'},
        {0, 0, 0, 0}
    };

    char *options_string = "s:vh";

    while ((o = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) {
        switch (o) {
            case 's':
                FREE(socket_path);
                socket_path = strdup(optarg);
                break;
            case 'v':
                printf("i3-config-wizard " I3_VERSION "\n");
                return 0;
            case 'h':
                printf("i3-config-wizard " I3_VERSION "\n");
                printf("i3-config-wizard [-s <socket>] [-v]\n");
                return 0;
        }
    }

    /* Check if the destination config file does not exist but the path is
     * writable. If not, exit now, this program is not useful in that case. */
    struct stat stbuf;
    if (stat(config_path, &stbuf) == 0) {
        printf("The config file \"%s\" already exists. Exiting.\n", config_path);
        return 0;
    }

    /* Create ~/.i3 if it does not yet exist */
    char *config_dir = resolve_tilde("~/.i3");
    if (stat(config_dir, &stbuf) != 0)
        if (mkdir(config_dir, 0755) == -1)
            err(1, "mkdir(%s) failed", config_dir);
    free(config_dir);

    int fd;
    if ((fd = open(config_path, O_CREAT | O_RDWR, 0644)) == -1) {
        printf("Cannot open file \"%s\" for writing: %s. Exiting.\n", config_path, strerror(errno));
        return 0;
    }
    close(fd);
    unlink(config_path);

    if (socket_path == NULL)
        socket_path = root_atom_contents("I3_SOCKET_PATH");

    if (socket_path == NULL)
        socket_path = "/tmp/i3-ipc.sock";

    int screens;
    if ((conn = xcb_connect(NULL, &screens)) == NULL ||
        xcb_connection_has_error(conn))
        errx(1, "Cannot open display\n");

    xcb_get_modifier_mapping_cookie_t modmap_cookie;
    modmap_cookie = xcb_get_modifier_mapping(conn);
    symbols = xcb_key_symbols_alloc(conn);

    /* Place requests for the atoms we need as soon as possible */
    #define xmacro(atom) \
        xcb_intern_atom_cookie_t atom ## _cookie = xcb_intern_atom(conn, 0, strlen(#atom), #atom);
    #include "atoms.xmacro"
    #undef xmacro

    root_screen = xcb_aux_get_screen(conn, screens);
    root = root_screen->root;

    if (!(modmap_reply = xcb_get_modifier_mapping_reply(conn, modmap_cookie, NULL)))
        errx(EXIT_FAILURE, "Could not get modifier mapping\n");

    xcb_numlock_mask = get_mod_mask_for(XCB_NUM_LOCK, symbols, modmap_reply);

    font = load_font(pattern, true);
    bold_font = load_font(patternbold, true);

    /* Open an input window */
    win = xcb_generate_id(conn);
    xcb_create_window(
        conn,
        XCB_COPY_FROM_PARENT,
        win, /* the window id */
        root, /* parent == root */
        490, 297, 300, 205, /* dimensions */
        0, /* X11 border = 0, we draw our own */
        XCB_WINDOW_CLASS_INPUT_OUTPUT,
        XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */
        XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK,
        (uint32_t[]){
            0, /* back pixel: black */
            XCB_EVENT_MASK_EXPOSURE |
            XCB_EVENT_MASK_BUTTON_PRESS
        });

    /* Map the window (make it visible) */
    xcb_map_window(conn, win);

    /* Setup NetWM atoms */
    #define xmacro(name) \
        do { \
            xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(conn, name ## _cookie, NULL); \
            if (!reply) \
                errx(EXIT_FAILURE, "Could not get atom " # name "\n"); \
            \
            A_ ## name = reply->atom; \
            free(reply); \
        } while (0);
    #include "atoms.xmacro"
    #undef xmacro

    /* Set dock mode */
    xcb_change_property(conn,
        XCB_PROP_MODE_REPLACE,
        win,
        A__NET_WM_WINDOW_TYPE,
        A_ATOM,
        32,
        1,
        (unsigned char*) &A__NET_WM_WINDOW_TYPE_DIALOG);

    /* Set window title */
    xcb_change_property(conn,
        XCB_PROP_MODE_REPLACE,
        win,
        A__NET_WM_NAME,
        A_UTF8_STRING,
        8,
        strlen("i3: first configuration"),
        "i3: first configuration");

    /* Create pixmap */
    pixmap = xcb_generate_id(conn);
    pixmap_gc = xcb_generate_id(conn);
    xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, 500, 500);
    xcb_create_gc(conn, pixmap_gc, pixmap, 0, 0);

    /* Grab the keyboard to get all input */
    xcb_flush(conn);

    /* Try (repeatedly, if necessary) to grab the keyboard. We might not
     * get the keyboard at the first attempt because of the keybinding
     * still being active when started via a wm’s keybinding. */
    xcb_grab_keyboard_cookie_t cookie;
    xcb_grab_keyboard_reply_t *reply = NULL;

    int count = 0;
    while ((reply == NULL || reply->status != XCB_GRAB_STATUS_SUCCESS) && (count++ < 500)) {
        cookie = xcb_grab_keyboard(conn, false, win, XCB_CURRENT_TIME, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
        reply = xcb_grab_keyboard_reply(conn, cookie, NULL);
        usleep(1000);
    }

    if (reply->status != XCB_GRAB_STATUS_SUCCESS) {
        fprintf(stderr, "Could not grab keyboard, status = %d\n", reply->status);
        exit(-1);
    }

    xcb_flush(conn);

    xcb_generic_event_t *event;
    while ((event = xcb_wait_for_event(conn)) != NULL) {
        if (event->response_type == 0) {
            fprintf(stderr, "X11 Error received! sequence %x\n", event->sequence);
            continue;
        }

        /* Strip off the highest bit (set if the event is generated) */
        int type = (event->response_type & 0x7F);

        switch (type) {
            case XCB_KEY_PRESS:
                handle_key_press(NULL, conn, (xcb_key_press_event_t*)event);
                break;

            /* TODO: handle mappingnotify */

            case XCB_BUTTON_PRESS:
                handle_button_press((xcb_button_press_event_t*)event);
                break;

            case XCB_EXPOSE:
                handle_expose();
                break;
        }

        free(event);
    }

    return 0;
}
コード例 #23
0
ファイル: display_version.c プロジェクト: Acidburn0zzz/i3
/*
 * Connects to i3 to find out the currently running version. Useful since it
 * might be different from the version compiled into this binary (maybe the
 * user didn’t correctly install i3 or forgot te restart it).
 *
 * The output looks like this:
 * Running i3 version: 4.2-202-gb8e782c (2012-08-12, branch "next") (pid 14804)
 *
 * The i3 binary you just called: /home/michael/i3/i3
 * The i3 binary you are running: /home/michael/i3/i3
 *
 */
void display_running_version(void) {
    char *socket_path = root_atom_contents("I3_SOCKET_PATH", conn, conn_screen);
    if (socket_path == NULL)
        exit(EXIT_SUCCESS);

    char *pid_from_atom = root_atom_contents("I3_PID", conn, conn_screen);
    if (pid_from_atom == NULL) {
        /* If I3_PID is not set, the running version is older than 4.2-200. */
        printf("\nRunning version: < 4.2-200\n");
        exit(EXIT_SUCCESS);
    }

    /* Inform the user of what we are doing. While a single IPC request is
     * really fast normally, in case i3 hangs, this will not terminate. */
    printf("(Getting version from running i3, press ctrl-c to abort…)");
    fflush(stdout);

    /* TODO: refactor this with the code for sending commands */
    int sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
    if (sockfd == -1)
        err(EXIT_FAILURE, "Could not create socket");

    struct sockaddr_un addr;
    memset(&addr, 0, sizeof(struct sockaddr_un));
    addr.sun_family = AF_LOCAL;
    strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);
    if (connect(sockfd, (const struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0)
        err(EXIT_FAILURE, "Could not connect to i3");

    if (ipc_send_message(sockfd, 0, I3_IPC_MESSAGE_TYPE_GET_VERSION,
                         (uint8_t *)"") == -1)
        err(EXIT_FAILURE, "IPC: write()");

    uint32_t reply_length;
    uint32_t reply_type;
    uint8_t *reply;
    int ret;
    if ((ret = ipc_recv_message(sockfd, &reply_type, &reply_length, &reply)) != 0) {
        if (ret == -1)
            err(EXIT_FAILURE, "IPC: read()");
        exit(EXIT_FAILURE);
    }

    if (reply_type != I3_IPC_MESSAGE_TYPE_GET_VERSION)
        errx(EXIT_FAILURE, "Got reply type %d, but expected %d (GET_VERSION)", reply_type, I3_IPC_MESSAGE_TYPE_GET_VERSION);

    yajl_handle handle = yajl_alloc(&version_callbacks, NULL, NULL);

    yajl_status state = yajl_parse(handle, (const unsigned char *)reply, (int)reply_length);
    if (state != yajl_status_ok)
        errx(EXIT_FAILURE, "Could not parse my own reply. That's weird. reply is %.*s", (int)reply_length, reply);

    printf("\rRunning i3 version: %s (pid %s)\n", human_readable_version, pid_from_atom);

    if (loaded_config_file_name) {
        struct stat sb;
        time_t now;
        char mtime[64];
        printf("Loaded i3 config: %s", loaded_config_file_name);
        if (stat(loaded_config_file_name, &sb) == -1) {
            printf("\n");
            ELOG("Cannot stat config file \"%s\"\n", loaded_config_file_name);
        } else {
            strftime(mtime, sizeof(mtime), "%c", localtime(&(sb.st_mtime)));
            time(&now);
            printf(" (Last modified: %s, %.f seconds ago)\n", mtime, difftime(now, sb.st_mtime));
        }
    }

#ifdef __linux__
    size_t destpath_size = 1024;
    ssize_t linksize;
    char *exepath;
    char *destpath = smalloc(destpath_size);

    sasprintf(&exepath, "/proc/%d/exe", getpid());

    while ((linksize = readlink(exepath, destpath, destpath_size)) == (ssize_t)destpath_size) {
        destpath_size = destpath_size * 2;
        destpath = srealloc(destpath, destpath_size);
    }
    if (linksize == -1)
        err(EXIT_FAILURE, "readlink(%s)", exepath);

    /* readlink() does not NULL-terminate strings, so we have to. */
    destpath[linksize] = '\0';

    printf("\n");
    printf("The i3 binary you just called: %s\n", destpath);

    free(exepath);
    sasprintf(&exepath, "/proc/%s/exe", pid_from_atom);

    while ((linksize = readlink(exepath, destpath, destpath_size)) == (ssize_t)destpath_size) {
        destpath_size = destpath_size * 2;
        destpath = srealloc(destpath, destpath_size);
    }
    if (linksize == -1)
        err(EXIT_FAILURE, "readlink(%s)", exepath);

    /* readlink() does not NULL-terminate strings, so we have to. */
    destpath[linksize] = '\0';

    /* Check if "(deleted)" is the readlink result. If so, the running version
     * does not match the file on disk. */
    if (strstr(destpath, "(deleted)") != NULL)
        printf("RUNNING BINARY DIFFERENT FROM BINARY ON DISK!\n");

    /* Since readlink() might put a "(deleted)" somewhere in the buffer and
     * stripping that out seems hackish and ugly, we read the process’s argv[0]
     * instead. */
    free(exepath);
    sasprintf(&exepath, "/proc/%s/cmdline", pid_from_atom);

    int fd;
    if ((fd = open(exepath, O_RDONLY)) == -1)
        err(EXIT_FAILURE, "open(%s)", exepath);
    if (read(fd, destpath, sizeof(destpath)) == -1)
        err(EXIT_FAILURE, "read(%s)", exepath);
    close(fd);

    printf("The i3 binary you are running: %s\n", destpath);

    free(exepath);
    free(destpath);
#endif

    yajl_free(handle);
}
コード例 #24
0
ファイル: main.c プロジェクト: fernandoataoldotcom/i3
int main(int argc, char *argv[]) {
    format = sstrdup("%s");
    char *socket_path = NULL;
    char *pattern = sstrdup("pango:monospace 8");
    int o, option_index = 0;

    static struct option long_options[] = {
        {"socket", required_argument, 0, 's'},
        {"version", no_argument, 0, 'v'},
        {"limit", required_argument, 0, 'l'},
        {"prompt", required_argument, 0, 'P'},
        {"prefix", required_argument, 0, 'p'},
        {"format", required_argument, 0, 'F'},
        {"font", required_argument, 0, 'f'},
        {"help", no_argument, 0, 'h'},
        {0, 0, 0, 0}};

    char *options_string = "s:p:P:f:l:F:vh";

    while ((o = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) {
        switch (o) {
            case 's':
                FREE(socket_path);
                socket_path = sstrdup(optarg);
                break;
            case 'v':
                printf("i3-input " I3_VERSION);
                return 0;
            case 'p':
                /* This option is deprecated, but will still work in i3 v4.1, 4.2 and 4.3 */
                fprintf(stderr, "i3-input: WARNING: the -p option is DEPRECATED in favor of the -F (format) option\n");
                FREE(format);
                sasprintf(&format, "%s%%s", optarg);
                break;
            case 'l':
                limit = atoi(optarg);
                break;
            case 'P':
                i3string_free(prompt);
                prompt = i3string_from_utf8(optarg);
                break;
            case 'f':
                FREE(pattern);
                pattern = sstrdup(optarg);
                break;
            case 'F':
                FREE(format);
                format = sstrdup(optarg);
                break;
            case 'h':
                printf("i3-input " I3_VERSION "\n");
                printf("i3-input [-s <socket>] [-F <format>] [-l <limit>] [-P <prompt>] [-f <font>] [-v]\n");
                printf("\n");
                printf("Example:\n");
                printf("    i3-input -F 'workspace \"%%s\"' -P 'Switch to workspace: '\n");
                return 0;
        }
    }

    printf("using format \"%s\"\n", format);

    int screen;
    conn = xcb_connect(NULL, &screen);
    if (!conn || xcb_connection_has_error(conn))
        die("Cannot open display\n");

    sockfd = ipc_connect(socket_path);

    root_screen = xcb_aux_get_screen(conn, screen);
    root = root_screen->root;

    symbols = xcb_key_symbols_alloc(conn);

    init_dpi();
    font = load_font(pattern, true);
    set_font(&font);

    if (prompt != NULL)
        prompt_offset = predict_text_width(prompt);

    const xcb_rectangle_t win_pos = get_window_position();

    /* Open an input window */
    win = xcb_generate_id(conn);
    xcb_create_window(
        conn,
        XCB_COPY_FROM_PARENT,
        win,                                                 /* the window id */
        root,                                                /* parent == root */
        win_pos.x, win_pos.y, win_pos.width, win_pos.height, /* dimensions */
        0,                                                   /* X11 border = 0, we draw our own */
        XCB_WINDOW_CLASS_INPUT_OUTPUT,
        XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */
        XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK,
        (uint32_t[]){
            0, /* back pixel: black */
            1, /* override redirect: don’t manage this window */
            XCB_EVENT_MASK_EXPOSURE});

    /* Map the window (make it visible) */
    xcb_map_window(conn, win);

    /* Initialize the drawable surface */
    draw_util_surface_init(conn, &surface, win, get_visualtype(root_screen), win_pos.width, win_pos.height);

    /* Grab the keyboard to get all input */
    xcb_flush(conn);

    /* Try (repeatedly, if necessary) to grab the keyboard. We might not
     * get the keyboard at the first attempt because of the keybinding
     * still being active when started via a wm’s keybinding. */
    xcb_grab_keyboard_cookie_t cookie;
    xcb_grab_keyboard_reply_t *reply = NULL;

    int count = 0;
    while ((reply == NULL || reply->status != XCB_GRAB_STATUS_SUCCESS) && (count++ < 500)) {
        cookie = xcb_grab_keyboard(conn, false, win, XCB_CURRENT_TIME, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
        reply = xcb_grab_keyboard_reply(conn, cookie, NULL);
        usleep(1000);
    }

    if (reply->status != XCB_GRAB_STATUS_SUCCESS) {
        fprintf(stderr, "Could not grab keyboard, status = %d\n", reply->status);
        exit(-1);
    }

    xcb_flush(conn);

    xcb_generic_event_t *event;
    while ((event = xcb_wait_for_event(conn)) != NULL) {
        if (event->response_type == 0) {
            fprintf(stderr, "X11 Error received! sequence %x\n", event->sequence);
            continue;
        }

        /* Strip off the highest bit (set if the event is generated) */
        int type = (event->response_type & 0x7F);

        switch (type) {
            case XCB_KEY_PRESS:
                handle_key_press(NULL, conn, (xcb_key_press_event_t *)event);
                break;

            case XCB_KEY_RELEASE:
                handle_key_release(NULL, conn, (xcb_key_release_event_t *)event);
                break;

            case XCB_EXPOSE:
                if (((xcb_expose_event_t *)event)->count == 0) {
                    handle_expose(NULL, conn, (xcb_expose_event_t *)event);
                }

                break;
        }

        free(event);
    }

    draw_util_surface_free(conn, &surface);
    return 0;
}
コード例 #25
0
ファイル: config.c プロジェクト: smrt28/i3
/*
 * Parse a string
 *
 */
static int config_string_cb(void *params_, const unsigned char *val, size_t _len) {
    int len = (int)_len;
    /* The id and socket_path are ignored, we already know them. */
    if (!strcmp(cur_key, "id") || !strcmp(cur_key, "socket_path"))
        return 1;

    if (!strcmp(cur_key, "mode")) {
        DLOG("mode = %.*s, len = %d\n", len, val, len);
        config.hide_on_modifier = (len == 4 && !strncmp((const char *)val, "dock", strlen("dock")) ? M_DOCK
                                                                                                   : (len == 4 && !strncmp((const char *)val, "hide", strlen("hide")) ? M_HIDE
                                                                                                                                                                      : M_INVISIBLE));
        return 1;
    }

    if (!strcmp(cur_key, "hidden_state")) {
        DLOG("hidden_state = %.*s, len = %d\n", len, val, len);
        config.hidden_state = (len == 4 && !strncmp((const char *)val, "hide", strlen("hide")) ? S_HIDE : S_SHOW);
        return 1;
    }

    if (!strcmp(cur_key, "modifier")) {
        DLOG("modifier = %.*s\n", len, val);
        if (len == 5 && !strncmp((const char *)val, "shift", strlen("shift"))) {
            config.modifier = ShiftMask;
            return 1;
        }
        if (len == 4 && !strncmp((const char *)val, "ctrl", strlen("ctrl"))) {
            config.modifier = ControlMask;
            return 1;
        }
        if (len == 4 && !strncmp((const char *)val, "Mod", strlen("Mod"))) {
            switch (val[3]) {
                case '1':
                    config.modifier = Mod1Mask;
                    return 1;
                case '2':
                    config.modifier = Mod2Mask;
                    return 1;
                case '3':
                    config.modifier = Mod3Mask;
                    return 1;
                /*
                case '4':
                    config.modifier = Mod4Mask;
                    return 1;
                */
                case '5':
                    config.modifier = Mod5Mask;
                    return 1;
            }
        }
        config.modifier = Mod4Mask;
        return 1;
    }

    if (!strcmp(cur_key, "wheel_up_cmd")) {
        DLOG("wheel_up_cmd = %.*s\n", len, val);
        FREE(config.wheel_up_cmd);
        sasprintf(&config.wheel_up_cmd, "%.*s", len, val);
        return 1;
    }

    if (!strcmp(cur_key, "wheel_down_cmd")) {
        DLOG("wheel_down_cmd = %.*s\n", len, val);
        FREE(config.wheel_down_cmd);
        sasprintf(&config.wheel_down_cmd, "%.*s", len, val);
        return 1;
    }

    if (!strcmp(cur_key, "position")) {
        DLOG("position = %.*s\n", len, val);
        config.position = (len == 3 && !strncmp((const char *)val, "top", strlen("top")) ? POS_TOP : POS_BOT);
        return 1;
    }

    if (!strcmp(cur_key, "status_command")) {
        DLOG("command = %.*s\n", len, val);
        sasprintf(&config.command, "%.*s", len, val);
        return 1;
    }

    if (!strcmp(cur_key, "font")) {
        DLOG("font = %.*s\n", len, val);
        sasprintf(&config.fontname, "%.*s", len, val);
        return 1;
    }

    if (!strcmp(cur_key, "outputs")) {
        DLOG("+output %.*s\n", len, val);
        int new_num_outputs = config.num_outputs + 1;
        config.outputs = srealloc(config.outputs, sizeof(char *) * new_num_outputs);
        sasprintf(&config.outputs[config.num_outputs], "%.*s", len, val);
        config.num_outputs = new_num_outputs;
        return 1;
    }

    if (!strcmp(cur_key, "tray_output")) {
        DLOG("tray_output %.*s\n", len, val);
        FREE(config.tray_output);
        sasprintf(&config.tray_output, "%.*s", len, val);
        return 1;
    }

#define COLOR(json_name, struct_name)                                  \
    do {                                                               \
        if (!strcmp(cur_key, #json_name)) {                            \
            DLOG(#json_name " = " #struct_name " = %.*s\n", len, val); \
            sasprintf(&(config.colors.struct_name), "%.*s", len, val); \
            return 1;                                                  \
        }                                                              \
    } while (0)

    COLOR(statusline, bar_fg);
    COLOR(background, bar_bg);
    COLOR(separator, sep_fg);
    COLOR(focused_workspace_border, focus_ws_border);
    COLOR(focused_workspace_bg, focus_ws_bg);
    COLOR(focused_workspace_text, focus_ws_fg);
    COLOR(active_workspace_border, active_ws_border);
    COLOR(active_workspace_bg, active_ws_bg);
    COLOR(active_workspace_text, active_ws_fg);
    COLOR(inactive_workspace_border, inactive_ws_border);
    COLOR(inactive_workspace_bg, inactive_ws_bg);
    COLOR(inactive_workspace_text, inactive_ws_fg);
    COLOR(urgent_workspace_border, urgent_ws_border);
    COLOR(urgent_workspace_bg, urgent_ws_bg);
    COLOR(urgent_workspace_text, urgent_ws_fg);

    printf("got unexpected string %.*s for cur_key = %s\n", len, val, cur_key);

    return 0;
}
コード例 #26
0
ファイル: log.c プロジェクト: bsmr-i3/i3
/*
 * Opens the logbuffer.
 *
 */
void open_logbuffer(void) {
    /* Reserve 1% of the RAM for the logfile, but at max 25 MiB.
         * For 512 MiB of RAM this will lead to a 5 MiB log buffer.
         * At the moment (2011-12-10), no testcase leads to an i3 log
         * of more than ~ 600 KiB. */
    logbuffer_size = min(physical_mem_bytes * 0.01, shmlog_size);
#if defined(__FreeBSD__)
    sasprintf(&shmlogname, "/tmp/i3-log-%d", getpid());
#else
    sasprintf(&shmlogname, "/i3-log-%d", getpid());
#endif
    logbuffer_shm = shm_open(shmlogname, O_RDWR | O_CREAT, S_IREAD | S_IWRITE);
    if (logbuffer_shm == -1) {
        fprintf(stderr, "Could not shm_open SHM segment for the i3 log: %s\n", strerror(errno));
        return;
    }

#if defined(__OpenBSD__) || defined(__APPLE__)
    if (ftruncate(logbuffer_shm, logbuffer_size) == -1) {
        fprintf(stderr, "Could not ftruncate SHM segment for the i3 log: %s\n", strerror(errno));
#else
    int ret;
    if ((ret = posix_fallocate(logbuffer_shm, 0, logbuffer_size)) != 0) {
        fprintf(stderr, "Could not ftruncate SHM segment for the i3 log: %s\n", strerror(ret));
#endif
        close(logbuffer_shm);
        shm_unlink(shmlogname);
        return;
    }

    logbuffer = mmap(NULL, logbuffer_size, PROT_READ | PROT_WRITE, MAP_SHARED, logbuffer_shm, 0);
    if (logbuffer == MAP_FAILED) {
        close_logbuffer();
        fprintf(stderr, "Could not mmap SHM segment for the i3 log: %s\n", strerror(errno));
        return;
    }

    /* Initialize with 0-bytes, just to be sure… */
    memset(logbuffer, '\0', logbuffer_size);

    header = (i3_shmlog_header *)logbuffer;

#if !defined(__OpenBSD__)
    pthread_condattr_t cond_attr;
    pthread_condattr_init(&cond_attr);
    if (pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED) != 0)
        fprintf(stderr, "pthread_condattr_setpshared() failed, i3-dump-log -f will not work!\n");
    pthread_cond_init(&(header->condvar), &cond_attr);
#endif

    logwalk = logbuffer + sizeof(i3_shmlog_header);
    loglastwrap = logbuffer + logbuffer_size;
    store_log_markers();
}

/*
 * Closes the logbuffer.
 *
 */
void close_logbuffer(void) {
    close(logbuffer_shm);
    shm_unlink(shmlogname);
    free(shmlogname);
    logbuffer = NULL;
    shmlogname = "";
}
コード例 #27
0
ファイル: config_parser.c プロジェクト: smrt28/i3
struct ConfigResultIR *parse_config(const char *input, struct context *context) {
    /* Dump the entire config file into the debug log. We cannot just use
     * DLOG("%s", input); because one log message must not exceed 4 KiB. */
    const char *dumpwalk = input;
    int linecnt = 1;
    while (*dumpwalk != '\0') {
        char *next_nl = strchr(dumpwalk, '\n');
        if (next_nl != NULL) {
            DLOG("CONFIG(line %3d): %.*s\n", linecnt, (int)(next_nl - dumpwalk), dumpwalk);
            dumpwalk = next_nl + 1;
        } else {
            DLOG("CONFIG(line %3d): %s\n", linecnt, dumpwalk);
            break;
        }
        linecnt++;
    }
    state = INITIAL;
    statelist_idx = 1;

    /* A YAJL JSON generator used for formatting replies. */
    command_output.json_gen = yajl_gen_alloc(NULL);

    y(array_open);

    const char *walk = input;
    const size_t len = strlen(input);
    int c;
    const cmdp_token *token;
    bool token_handled;
    linecnt = 1;

// TODO: make this testable
#ifndef TEST_PARSER
    cfg_criteria_init(&current_match, &subcommand_output, INITIAL);
#endif

    /* The "<=" operator is intentional: We also handle the terminating 0-byte
     * explicitly by looking for an 'end' token. */
    while ((size_t)(walk - input) <= len) {
        /* Skip whitespace before every token, newlines are relevant since they
         * separate configuration directives. */
        while ((*walk == ' ' || *walk == '\t') && *walk != '\0')
            walk++;

        //printf("remaining input: %s\n", walk);

        cmdp_token_ptr *ptr = &(tokens[state]);
        token_handled = false;
        for (c = 0; c < ptr->n; c++) {
            token = &(ptr->array[c]);

            /* A literal. */
            if (token->name[0] == '\'') {
                if (strncasecmp(walk, token->name + 1, strlen(token->name) - 1) == 0) {
                    if (token->identifier != NULL)
                        push_string(token->identifier, token->name + 1);
                    walk += strlen(token->name) - 1;
                    next_state(token);
                    token_handled = true;
                    break;
                }
                continue;
            }

            if (strcmp(token->name, "number") == 0) {
                /* Handle numbers. We only accept decimal numbers for now. */
                char *end = NULL;
                errno = 0;
                long int num = strtol(walk, &end, 10);
                if ((errno == ERANGE && (num == LONG_MIN || num == LONG_MAX)) ||
                    (errno != 0 && num == 0))
                    continue;

                /* No valid numbers found */
                if (end == walk)
                    continue;

                if (token->identifier != NULL)
                    push_long(token->identifier, num);

                /* Set walk to the first non-number character */
                walk = end;
                next_state(token);
                token_handled = true;
                break;
            }

            if (strcmp(token->name, "string") == 0 ||
                strcmp(token->name, "word") == 0) {
                const char *beginning = walk;
                /* Handle quoted strings (or words). */
                if (*walk == '"') {
                    beginning++;
                    walk++;
                    while (*walk != '\0' && (*walk != '"' || *(walk - 1) == '\\'))
                        walk++;
                } else {
                    if (token->name[0] == 's') {
                        while (*walk != '\0' && *walk != '\r' && *walk != '\n')
                            walk++;
                    } else {
                        /* For a word, the delimiters are white space (' ' or
                         * '\t'), closing square bracket (]), comma (,) and
                         * semicolon (;). */
                        while (*walk != ' ' && *walk != '\t' &&
                               *walk != ']' && *walk != ',' &&
                               *walk != ';' && *walk != '\r' &&
                               *walk != '\n' && *walk != '\0')
                            walk++;
                    }
                }
                if (walk != beginning) {
                    char *str = scalloc(walk - beginning + 1);
                    /* We copy manually to handle escaping of characters. */
                    int inpos, outpos;
                    for (inpos = 0, outpos = 0;
                         inpos < (walk - beginning);
                         inpos++, outpos++) {
                        /* We only handle escaped double quotes to not break
                         * backwards compatibility with people using \w in
                         * regular expressions etc. */
                        if (beginning[inpos] == '\\' && beginning[inpos + 1] == '"')
                            inpos++;
                        str[outpos] = beginning[inpos];
                    }
                    if (token->identifier)
                        push_string(token->identifier, str);
                    free(str);
                    /* If we are at the end of a quoted string, skip the ending
                     * double quote. */
                    if (*walk == '"')
                        walk++;
                    next_state(token);
                    token_handled = true;
                    break;
                }
            }

            if (strcmp(token->name, "line") == 0) {
                while (*walk != '\0' && *walk != '\n' && *walk != '\r')
                    walk++;
                next_state(token);
                token_handled = true;
                linecnt++;
                walk++;
                break;
            }

            if (strcmp(token->name, "end") == 0) {
                //printf("checking for end: *%s*\n", walk);
                if (*walk == '\0' || *walk == '\n' || *walk == '\r') {
                    next_state(token);
                    token_handled = true;
/* To make sure we start with an appropriate matching
                     * datastructure for commands which do *not* specify any
                     * criteria, we re-initialize the criteria system after
                     * every command. */
// TODO: make this testable
#ifndef TEST_PARSER
                    cfg_criteria_init(&current_match, &subcommand_output, INITIAL);
#endif
                    linecnt++;
                    walk++;
                    break;
                }
            }
        }

        if (!token_handled) {
            /* Figure out how much memory we will need to fill in the names of
             * all tokens afterwards. */
            int tokenlen = 0;
            for (c = 0; c < ptr->n; c++)
                tokenlen += strlen(ptr->array[c].name) + strlen("'', ");

            /* Build up a decent error message. We include the problem, the
             * full input, and underline the position where the parser
             * currently is. */
            char *errormessage;
            char *possible_tokens = smalloc(tokenlen + 1);
            char *tokenwalk = possible_tokens;
            for (c = 0; c < ptr->n; c++) {
                token = &(ptr->array[c]);
                if (token->name[0] == '\'') {
                    /* A literal is copied to the error message enclosed with
                     * single quotes. */
                    *tokenwalk++ = '\'';
                    strcpy(tokenwalk, token->name + 1);
                    tokenwalk += strlen(token->name + 1);
                    *tokenwalk++ = '\'';
                } else {
                    /* Skip error tokens in error messages, they are used
                     * internally only and might confuse users. */
                    if (strcmp(token->name, "error") == 0)
                        continue;
                    /* Any other token is copied to the error message enclosed
                     * with angle brackets. */
                    *tokenwalk++ = '<';
                    strcpy(tokenwalk, token->name);
                    tokenwalk += strlen(token->name);
                    *tokenwalk++ = '>';
                }
                if (c < (ptr->n - 1)) {
                    *tokenwalk++ = ',';
                    *tokenwalk++ = ' ';
                }
            }
            *tokenwalk = '\0';
            sasprintf(&errormessage, "Expected one of these tokens: %s",
                      possible_tokens);
            free(possible_tokens);

            /* Go back to the beginning of the line */
            const char *error_line = start_of_line(walk, input);

            /* Contains the same amount of characters as 'input' has, but with
             * the unparseable part highlighted using ^ characters. */
            char *position = scalloc(strlen(error_line) + 1);
            const char *copywalk;
            for (copywalk = error_line;
                 *copywalk != '\n' && *copywalk != '\r' && *copywalk != '\0';
                 copywalk++)
                position[(copywalk - error_line)] = (copywalk >= walk ? '^' : (*copywalk == '\t' ? '\t' : ' '));
            position[(copywalk - error_line)] = '\0';

            ELOG("CONFIG: %s\n", errormessage);
            ELOG("CONFIG: (in file %s)\n", context->filename);
            char *error_copy = single_line(error_line);

            /* Print context lines *before* the error, if any. */
            if (linecnt > 1) {
                const char *context_p1_start = start_of_line(error_line - 2, input);
                char *context_p1_line = single_line(context_p1_start);
                if (linecnt > 2) {
                    const char *context_p2_start = start_of_line(context_p1_start - 2, input);
                    char *context_p2_line = single_line(context_p2_start);
                    ELOG("CONFIG: Line %3d: %s\n", linecnt - 2, context_p2_line);
                    free(context_p2_line);
                }
                ELOG("CONFIG: Line %3d: %s\n", linecnt - 1, context_p1_line);
                free(context_p1_line);
            }
            ELOG("CONFIG: Line %3d: %s\n", linecnt, error_copy);
            ELOG("CONFIG:           %s\n", position);
            free(error_copy);
            /* Print context lines *after* the error, if any. */
            for (int i = 0; i < 2; i++) {
                char *error_line_end = strchr(error_line, '\n');
                if (error_line_end != NULL && *(error_line_end + 1) != '\0') {
                    error_line = error_line_end + 1;
                    error_copy = single_line(error_line);
                    ELOG("CONFIG: Line %3d: %s\n", linecnt + i + 1, error_copy);
                    free(error_copy);
                }
            }

            context->has_errors = true;

            /* Format this error message as a JSON reply. */
            y(map_open);
            ystr("success");
            y(bool, false);
            /* We set parse_error to true to distinguish this from other
             * errors. i3-nagbar is spawned upon keypresses only for parser
             * errors. */
            ystr("parse_error");
            y(bool, true);
            ystr("error");
            ystr(errormessage);
            ystr("input");
            ystr(input);
            ystr("errorposition");
            ystr(position);
            y(map_close);

            /* Skip the rest of this line, but continue parsing. */
            while ((size_t)(walk - input) <= len && *walk != '\n')
                walk++;

            free(position);
            free(errormessage);
            clear_stack();

            /* To figure out in which state to go (e.g. MODE or INITIAL),
             * we find the nearest state which contains an <error> token
             * and follow that one. */
            bool error_token_found = false;
            for (int i = statelist_idx - 1; (i >= 0) && !error_token_found; i--) {
                cmdp_token_ptr *errptr = &(tokens[statelist[i]]);
                for (int j = 0; j < errptr->n; j++) {
                    if (strcmp(errptr->array[j].name, "error") != 0)
                        continue;
                    next_state(&(errptr->array[j]));
                    error_token_found = true;
                    break;
                }
            }

            assert(error_token_found);
        }
    }